linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] RFC: HID: wiiu-drc: Add a driver for the Wii U gamepad
@ 2021-05-02 23:28 Emmanuel Gil Peyrot
  2021-05-02 23:28 ` [PATCH 1/4] HID: wiiu-drc: Add a driver for this gamepad Emmanuel Gil Peyrot
                   ` (4 more replies)
  0 siblings, 5 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-02 23:28 UTC (permalink / raw)
  To: linux-input
  Cc: Emmanuel Gil Peyrot, Ash Logan, Jonathan Neuschäfer,
	Jiri Kosina, Benjamin Tissoires, linux-kernel

This driver is for the DRC (wireless gamepad) when plugged to the DRH of
the Wii U, a chip exposing it as a USB device.

I tried to use this driver on master over usbip on my laptop, but usbip
disconnects the device right after the driver created the
/dev/input/event* files, so instead I have only tested this driver on
the 4.19 branch of the linux-wiiu[1] downstream.

Other than that, pretty much all of the HID parts of the gamepad work,
it’s only missing microphone, camera and NFC input now but those are
mostly standard (read require quirks) and pertain to other subsystems,
so I felt like this can be upstreamed already.

I’ve still put the RFC tag on this pull request because of two known
problems in these patches (annotated with TODOs in the code):
- The magnetometer is exposed using non-sensical ABS_* values, it seems
  most (all?) magnetometers are exposed in the iio subsystem instead,
  should I go the same way despite it clearly being part of the same HID
  device?
- The battery number is currently based on a static int being
  incremented every time a new gamepad is “plugged in”, while I’d prefer
  to reuse the interface number for that.

Thanks for your guidance. :)

[1] https://gitlab.com/linux-wiiu/linux-wiiu

Ash Logan (1):
  HID: wiiu-drc: Add a driver for this gamepad

Emmanuel Gil Peyrot (3):
  HID: wiiu-drc: Implement touch reports
  HID: wiiu-drc: Add accelerometer, gyroscope and magnetometer readings
  HID: wiiu-drc: Add battery reporting

 drivers/hid/Kconfig        |   7 +
 drivers/hid/Makefile       |   1 +
 drivers/hid/hid-ids.h      |   1 +
 drivers/hid/hid-quirks.c   |   3 +
 drivers/hid/hid-wiiu-drc.c | 522 +++++++++++++++++++++++++++++++++++++
 5 files changed, 534 insertions(+)
 create mode 100644 drivers/hid/hid-wiiu-drc.c

-- 
2.31.1


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

* [PATCH 1/4] HID: wiiu-drc: Add a driver for this gamepad
  2021-05-02 23:28 [PATCH 0/4] RFC: HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
@ 2021-05-02 23:28 ` Emmanuel Gil Peyrot
  2021-05-05 22:33   ` Jonathan Neuschäfer
  2021-05-06 11:53   ` Barnabás Pőcze
  2021-05-02 23:28 ` [PATCH 2/4] HID: wiiu-drc: Implement touch reports Emmanuel Gil Peyrot
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-02 23:28 UTC (permalink / raw)
  To: linux-input
  Cc: Ash Logan, Jonathan Neuschäfer, Jiri Kosina,
	Benjamin Tissoires, linux-kernel, Emmanuel Gil Peyrot

From: Ash Logan <ash@heyquark.com>

This driver is for the DRC (wireless gamepad) when plugged to the DRH of
the Wii U, a chip exposing it as a USB device.

This first patch exposes the buttons and sticks of this device, so that
it can act as a plain game controller.

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/Kconfig        |   7 +
 drivers/hid/Makefile       |   1 +
 drivers/hid/hid-ids.h      |   1 +
 drivers/hid/hid-quirks.c   |   3 +
 drivers/hid/hid-wiiu-drc.c | 270 +++++++++++++++++++++++++++++++++++++
 5 files changed, 282 insertions(+)
 create mode 100644 drivers/hid/hid-wiiu-drc.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 4bf263c2d61a..01116c315459 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1105,6 +1105,13 @@ config HID_WACOM
 	  To compile this driver as a module, choose M here: the
 	  module will be called wacom.
 
+config HID_WIIU_DRC
+	tristate "Nintendo Wii U gamepad over internal DRH"
+	depends on HID
+	help
+	  Support for the Wii U gamepad, when connected with the Wii U’s
+	  internal DRH chip.
+
 config HID_WIIMOTE
 	tristate "Nintendo Wii / Wii U peripherals"
 	depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 193431ec4db8..8fcaaeae4d65 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -134,6 +134,7 @@ wacom-objs			:= wacom_wac.o wacom_sys.o
 obj-$(CONFIG_HID_WACOM)		+= wacom.o
 obj-$(CONFIG_HID_WALTOP)	+= hid-waltop.o
 obj-$(CONFIG_HID_WIIMOTE)	+= hid-wiimote.o
+obj-$(CONFIG_HID_WIIU_DRC)	+= hid-wiiu-drc.o
 obj-$(CONFIG_HID_SENSOR_HUB)	+= hid-sensor-hub.o
 obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR)	+= hid-sensor-custom.o
 
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 84b8da3e7d09..fbac0dd021f1 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -916,6 +916,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_WIIU_DRH	0x0341
 
 #define USB_VENDOR_ID_NOVATEK		0x0603
 #define USB_DEVICE_ID_NOVATEK_PCT	0x0600
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 3dd6f15f2a67..af400177537e 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -513,6 +513,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
 #endif
+#if IS_ENABLED(CONFIG_HID_WIIU_DRC)
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
+#endif
 #if IS_ENABLED(CONFIG_HID_NTI)
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NTI, USB_DEVICE_ID_USB_SUN) },
 #endif
diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
new file mode 100644
index 000000000000..018cbdb53a2c
--- /dev/null
+++ b/drivers/hid/hid-wiiu-drc.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID driver for Nintendo Wii U gamepad, connected via console-internal DRH
+ *
+ * Copyright (C) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+ * Copyright (C) 2019 Ash Logan <ash@heyquark.com>
+ * Copyright (C) 2013 Mema Hacking
+ *
+ * Based on the excellent work at http://libdrc.org/docs/re/sc-input.html and
+ * https://bitbucket.org/memahaxx/libdrc/src/master/src/input-receiver.cpp .
+ * libdrc code is licensed under BSD 2-Clause.
+ * Driver based on hid-udraw-ps3.c.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include "hid-ids.h"
+
+#define DEVICE_NAME	"Nintendo Wii U gamepad"
+
+/* Button and stick constants */
+#define VOLUME_MIN	0
+#define VOLUME_MAX	255
+#define NUM_STICK_AXES	4
+#define STICK_MIN	900
+#define STICK_MAX	3200
+
+#define BUTTON_SYNC	BIT(0)
+#define BUTTON_HOME	BIT(1)
+#define BUTTON_MINUS	BIT(2)
+#define BUTTON_PLUS	BIT(3)
+#define BUTTON_R	BIT(4)
+#define BUTTON_L	BIT(5)
+#define BUTTON_ZR	BIT(6)
+#define BUTTON_ZL	BIT(7)
+#define BUTTON_DOWN	BIT(8)
+#define BUTTON_UP	BIT(9)
+#define BUTTON_RIGHT	BIT(10)
+#define BUTTON_LEFT	BIT(11)
+#define BUTTON_Y	BIT(12)
+#define BUTTON_X	BIT(13)
+#define BUTTON_B	BIT(14)
+#define BUTTON_A	BIT(15)
+
+#define BUTTON_TV	BIT(21)
+#define BUTTON_R3	BIT(22)
+#define BUTTON_L3	BIT(23)
+
+#define BUTTON_POWER	BIT(25)
+
+/*
+ * The device is setup with multiple input devices:
+ * - A joypad with the buttons and sticks.
+ */
+
+struct drc {
+	struct input_dev *joy_input_dev;
+	struct hid_device *hdev;
+};
+
+static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
+	 u8 *data, int len)
+{
+	struct drc *drc = hid_get_drvdata(hdev);
+	int i;
+	u32 buttons;
+
+	if (len != 128)
+		return 0;
+
+	buttons = (data[4] << 24) | (data[80] << 16) | (data[2] << 8) | data[3];
+	/* joypad */
+	input_report_key(drc->joy_input_dev, BTN_DPAD_RIGHT, buttons & BUTTON_RIGHT);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_DOWN, buttons & BUTTON_DOWN);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_LEFT, buttons & BUTTON_LEFT);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_UP, buttons & BUTTON_UP);
+
+	input_report_key(drc->joy_input_dev, BTN_EAST, buttons & BUTTON_A);
+	input_report_key(drc->joy_input_dev, BTN_SOUTH, buttons & BUTTON_B);
+	input_report_key(drc->joy_input_dev, BTN_NORTH, buttons & BUTTON_X);
+	input_report_key(drc->joy_input_dev, BTN_WEST, buttons & BUTTON_Y);
+
+	input_report_key(drc->joy_input_dev, BTN_TL, buttons & BUTTON_L);
+	input_report_key(drc->joy_input_dev, BTN_TL2, buttons & BUTTON_ZL);
+	input_report_key(drc->joy_input_dev, BTN_TR, buttons & BUTTON_R);
+	input_report_key(drc->joy_input_dev, BTN_TR2, buttons & BUTTON_ZR);
+
+	input_report_key(drc->joy_input_dev, BTN_Z, buttons & BUTTON_TV);
+	input_report_key(drc->joy_input_dev, BTN_THUMBL, buttons & BUTTON_L3);
+	input_report_key(drc->joy_input_dev, BTN_THUMBR, buttons & BUTTON_R3);
+
+	input_report_key(drc->joy_input_dev, BTN_SELECT, buttons & BUTTON_MINUS);
+	input_report_key(drc->joy_input_dev, BTN_START, buttons & BUTTON_PLUS);
+	input_report_key(drc->joy_input_dev, BTN_MODE, buttons & BUTTON_HOME);
+
+	input_report_key(drc->joy_input_dev, BTN_DEAD, buttons & BUTTON_POWER);
+
+	for (i = 0; i < NUM_STICK_AXES; i++) {
+		s16 val = (data[7 + 2*i] << 8) | data[6 + 2*i];
+		/* clamp */
+		if (val < STICK_MIN)
+			val = STICK_MIN;
+		if (val > STICK_MAX)
+			val = STICK_MAX;
+
+		switch (i) {
+		case 0:
+			input_report_abs(drc->joy_input_dev, ABS_X, val);
+			break;
+		case 1:
+			input_report_abs(drc->joy_input_dev, ABS_Y, val);
+			break;
+		case 2:
+			input_report_abs(drc->joy_input_dev, ABS_RX, val);
+			break;
+		case 3:
+			input_report_abs(drc->joy_input_dev, ABS_RY, val);
+			break;
+		default:
+			break;
+		}
+	}
+
+	input_report_abs(drc->joy_input_dev, ABS_VOLUME, data[14]);
+
+	input_sync(drc->joy_input_dev);
+
+	/* let hidraw and hiddev handle the report */
+	return 0;
+}
+
+static int drc_open(struct input_dev *dev)
+{
+	struct drc *drc = input_get_drvdata(dev);
+
+	return hid_hw_open(drc->hdev);
+}
+
+static void drc_close(struct input_dev *dev)
+{
+	struct drc *drc = input_get_drvdata(dev);
+
+	hid_hw_close(drc->hdev);
+}
+
+static struct input_dev *allocate_and_setup(struct hid_device *hdev,
+		const char *name)
+{
+	struct input_dev *input_dev;
+
+	input_dev = devm_input_allocate_device(&hdev->dev);
+	if (!input_dev)
+		return NULL;
+
+	input_dev->name = name;
+	input_dev->phys = hdev->phys;
+	input_dev->dev.parent = &hdev->dev;
+	input_dev->open = drc_open;
+	input_dev->close = drc_close;
+	input_dev->uniq = hdev->uniq;
+	input_dev->id.bustype = hdev->bus;
+	input_dev->id.vendor  = hdev->vendor;
+	input_dev->id.product = hdev->product;
+	input_dev->id.version = hdev->version;
+	input_set_drvdata(input_dev, hid_get_drvdata(hdev));
+
+	return input_dev;
+}
+
+static bool drc_setup_joypad(struct drc *drc,
+		struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad");
+	if (!input_dev)
+		return false;
+
+	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	set_bit(BTN_DPAD_RIGHT, input_dev->keybit);
+	set_bit(BTN_DPAD_DOWN, input_dev->keybit);
+	set_bit(BTN_DPAD_LEFT, input_dev->keybit);
+	set_bit(BTN_DPAD_UP, input_dev->keybit);
+	set_bit(BTN_EAST, input_dev->keybit);
+	set_bit(BTN_SOUTH, input_dev->keybit);
+	set_bit(BTN_NORTH, input_dev->keybit);
+	set_bit(BTN_WEST, input_dev->keybit);
+	set_bit(BTN_TL, input_dev->keybit);
+	set_bit(BTN_TL2, input_dev->keybit);
+	set_bit(BTN_TR, input_dev->keybit);
+	set_bit(BTN_TR2, input_dev->keybit);
+	set_bit(BTN_THUMBL, input_dev->keybit);
+	set_bit(BTN_THUMBR, input_dev->keybit);
+	set_bit(BTN_SELECT, input_dev->keybit);
+	set_bit(BTN_START, input_dev->keybit);
+	set_bit(BTN_MODE, input_dev->keybit);
+
+	/* These two buttons are actually TV control and Power. */
+	set_bit(BTN_Z, input_dev->keybit);
+	set_bit(BTN_DEAD, input_dev->keybit);
+
+	input_set_abs_params(input_dev, ABS_X, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RX, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RY, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_VOLUME, VOLUME_MIN, VOLUME_MAX, 0, 0);
+
+	drc->joy_input_dev = input_dev;
+
+	return true;
+}
+
+static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	struct drc *drc;
+	int ret;
+
+	drc = devm_kzalloc(&hdev->dev, sizeof(struct drc), GFP_KERNEL);
+	if (!drc)
+		return -ENOMEM;
+
+	drc->hdev = hdev;
+
+	hid_set_drvdata(hdev, drc);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "parse failed\n");
+		return ret;
+	}
+
+	if (!drc_setup_joypad(drc, hdev)) {
+		hid_err(hdev, "could not allocate interface\n");
+		return -ENOMEM;
+	}
+
+	ret = input_register_device(drc->joy_input_dev);
+	if (ret) {
+		hid_err(hdev, "failed to register interface\n");
+		return ret;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER);
+	if (ret) {
+		hid_err(hdev, "hw start failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct hid_device_id drc_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, drc_devices);
+
+static struct hid_driver drc_driver = {
+	.name = "hid-wiiu-drc",
+	.id_table = drc_devices,
+	.raw_event = drc_raw_event,
+	.probe = drc_probe,
+};
+module_hid_driver(drc_driver);
+
+MODULE_AUTHOR("Ash Logan <ash@heyquark.com>");
+MODULE_DESCRIPTION("Nintendo Wii U gamepad driver");
+MODULE_LICENSE("GPL");
-- 
2.31.1


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

* [PATCH 2/4] HID: wiiu-drc: Implement touch reports
  2021-05-02 23:28 [PATCH 0/4] RFC: HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
  2021-05-02 23:28 ` [PATCH 1/4] HID: wiiu-drc: Add a driver for this gamepad Emmanuel Gil Peyrot
@ 2021-05-02 23:28 ` Emmanuel Gil Peyrot
  2021-05-05 22:43   ` Jonathan Neuschäfer
  2021-05-02 23:28 ` [PATCH 3/4] HID: wiiu-drc: Add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-02 23:28 UTC (permalink / raw)
  To: linux-input
  Cc: Emmanuel Gil Peyrot, Ash Logan, Jonathan Neuschäfer,
	Jiri Kosina, Benjamin Tissoires, linux-kernel

There is a 100×200 inaccessible border on each side, and the Y axis is
inverted, these are the two main quirks of this touch panel.

I’ve been testing with weston-simple-touch mostly, but it also with the
rest of Weston.

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-wiiu-drc.c | 83 +++++++++++++++++++++++++++++++++++---
 1 file changed, 78 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
index 018cbdb53a2c..77e70827c37d 100644
--- a/drivers/hid/hid-wiiu-drc.c
+++ b/drivers/hid/hid-wiiu-drc.c
@@ -49,13 +49,27 @@
 
 #define BUTTON_POWER	BIT(25)
 
+/* Touch constants */
+/* Resolution in pixels */
+#define RES_X		854
+#define RES_Y		480
+/* Display/touch size in mm */
+#define WIDTH		138
+#define HEIGHT		79
+#define NUM_TOUCH_POINTS 10
+#define MAX_TOUCH_RES	(1 << 12)
+#define TOUCH_BORDER_X	100
+#define TOUCH_BORDER_Y	200
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
+ * - The touch area which works as a touchscreen.
  */
 
 struct drc {
 	struct input_dev *joy_input_dev;
+	struct input_dev *touch_input_dev;
 	struct hid_device *hdev;
 };
 
@@ -63,7 +77,7 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 	 u8 *data, int len)
 {
 	struct drc *drc = hid_get_drvdata(hdev);
-	int i;
+	int i, x, y, pressure, base;
 	u32 buttons;
 
 	if (len != 128)
@@ -126,6 +140,37 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 
 	input_sync(drc->joy_input_dev);
 
+	/* touch */
+	/* Average touch points for improved accuracy. */
+	x = y = 0;
+	for (i = 0; i < NUM_TOUCH_POINTS; i++) {
+		base = 36 + 4 * i;
+
+		x += ((data[base + 1] & 0xF) << 8) | data[base];
+		y += ((data[base + 3] & 0xF) << 8) | data[base + 2];
+	}
+	x /= NUM_TOUCH_POINTS;
+	y /= NUM_TOUCH_POINTS;
+
+	/* Pressure reporting isn’t properly understood, so we don’t report it yet. */
+	pressure = 0;
+	pressure |= ((data[37] >> 4) & 7) << 0;
+	pressure |= ((data[39] >> 4) & 7) << 3;
+	pressure |= ((data[41] >> 4) & 7) << 6;
+	pressure |= ((data[43] >> 4) & 7) << 9;
+
+	if (pressure != 0) {
+		input_report_key(drc->touch_input_dev, BTN_TOUCH, 1);
+		input_report_key(drc->touch_input_dev, BTN_TOOL_FINGER, 1);
+
+		input_report_abs(drc->touch_input_dev, ABS_X, x);
+		input_report_abs(drc->touch_input_dev, ABS_Y, MAX_TOUCH_RES - y);
+	} else {
+		input_report_key(drc->touch_input_dev, BTN_TOUCH, 0);
+		input_report_key(drc->touch_input_dev, BTN_TOOL_FINGER, 0);
+	}
+	input_sync(drc->touch_input_dev);
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -168,6 +213,32 @@ static struct input_dev *allocate_and_setup(struct hid_device *hdev,
 	return input_dev;
 }
 
+static bool drc_setup_touch(struct drc *drc,
+		struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " Touch");
+	if (!input_dev)
+		return false;
+
+	input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+
+	input_set_abs_params(input_dev, ABS_X, TOUCH_BORDER_X, MAX_TOUCH_RES - TOUCH_BORDER_X, 20, 0);
+	input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH);
+	input_set_abs_params(input_dev, ABS_Y, TOUCH_BORDER_Y, MAX_TOUCH_RES - TOUCH_BORDER_Y, 20, 0);
+	input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT);
+
+	set_bit(BTN_TOUCH, input_dev->keybit);
+	set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+
+	set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+	drc->touch_input_dev = input_dev;
+
+	return true;
+}
+
 static bool drc_setup_joypad(struct drc *drc,
 		struct hid_device *hdev)
 {
@@ -231,14 +302,16 @@ static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		return ret;
 	}
 
-	if (!drc_setup_joypad(drc, hdev)) {
-		hid_err(hdev, "could not allocate interface\n");
+	if (!drc_setup_joypad(drc, hdev) ||
+	    !drc_setup_touch(drc, hdev)) {
+		hid_err(hdev, "could not allocate interfaces\n");
 		return -ENOMEM;
 	}
 
-	ret = input_register_device(drc->joy_input_dev);
+	ret = input_register_device(drc->joy_input_dev) ||
+		input_register_device(drc->touch_input_dev);
 	if (ret) {
-		hid_err(hdev, "failed to register interface\n");
+		hid_err(hdev, "failed to register interfaces\n");
 		return ret;
 	}
 
-- 
2.31.1


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

* [PATCH 3/4] HID: wiiu-drc: Add accelerometer, gyroscope and magnetometer readings
  2021-05-02 23:28 [PATCH 0/4] RFC: HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
  2021-05-02 23:28 ` [PATCH 1/4] HID: wiiu-drc: Add a driver for this gamepad Emmanuel Gil Peyrot
  2021-05-02 23:28 ` [PATCH 2/4] HID: wiiu-drc: Implement touch reports Emmanuel Gil Peyrot
@ 2021-05-02 23:28 ` Emmanuel Gil Peyrot
  2021-05-02 23:28 ` [PATCH 4/4] HID: wiiu-drc: Add battery reporting Emmanuel Gil Peyrot
  2021-05-19  8:59 ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
  4 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-02 23:28 UTC (permalink / raw)
  To: linux-input
  Cc: Emmanuel Gil Peyrot, Ash Logan, Jonathan Neuschäfer,
	Jiri Kosina, Benjamin Tissoires, linux-kernel

These are mostly untested so far, because I have no idea which userland
to test against, but evtest seems to give at least sensible values.

The magnetometer doesn’t have dedicated INPUT_PROP_ACCELEROMETER
buttons, so I used three clearly invalid absolute values, in the hope
that someone will fix that over time.  Another solution might be to go
for the iio subsystem instead, but it wouldn’t be tied to the HID any
longer and I would feel uneasy about that.  Especially because multiple
such gamepads could be connected to a single computer.

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-wiiu-drc.c | 78 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 75 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
index 77e70827c37d..80faaaad2bb5 100644
--- a/drivers/hid/hid-wiiu-drc.c
+++ b/drivers/hid/hid-wiiu-drc.c
@@ -61,15 +61,25 @@
 #define TOUCH_BORDER_X	100
 #define TOUCH_BORDER_Y	200
 
+/* Accelerometer, gyroscope and magnetometer constants */
+#define ACCEL_MIN	-(1 << 15)
+#define ACCEL_MAX	((1 << 15) - 1)
+#define GYRO_MIN	-(1 << 23)
+#define GYRO_MAX	((1 << 23) - 1)
+#define MAGNET_MIN	ACCEL_MIN
+#define MAGNET_MAX	ACCEL_MAX
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
  * - The touch area which works as a touchscreen.
+ * - An accelerometer + gyroscope + magnetometer device.
  */
 
 struct drc {
 	struct input_dev *joy_input_dev;
 	struct input_dev *touch_input_dev;
+	struct input_dev *accel_input_dev;
 	struct hid_device *hdev;
 };
 
@@ -77,7 +87,7 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 	 u8 *data, int len)
 {
 	struct drc *drc = hid_get_drvdata(hdev);
-	int i, x, y, pressure, base;
+	int i, x, y, z, pressure, base;
 	u32 buttons;
 
 	if (len != 128)
@@ -171,6 +181,31 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 	}
 	input_sync(drc->touch_input_dev);
 
+	/* accelerometer */
+	x = (data[16] << 8) | data[15];
+	y = (data[18] << 8) | data[17];
+	z = (data[20] << 8) | data[19];
+	input_report_abs(drc->accel_input_dev, ABS_X, (int16_t)x);
+	input_report_abs(drc->accel_input_dev, ABS_Y, (int16_t)y);
+	input_report_abs(drc->accel_input_dev, ABS_Z, (int16_t)z);
+
+	/* gyroscope */
+	x = (data[23] << 24) | (data[22] << 16) | (data[21] << 8);
+	y = (data[26] << 24) | (data[25] << 16) | (data[24] << 8);
+	z = (data[29] << 24) | (data[28] << 16) | (data[27] << 8);
+	input_report_abs(drc->accel_input_dev, ABS_RX, x >> 8);
+	input_report_abs(drc->accel_input_dev, ABS_RY, y >> 8);
+	input_report_abs(drc->accel_input_dev, ABS_RZ, z >> 8);
+
+	/* magnetometer */
+	x = (data[31] << 8) | data[30];
+	y = (data[33] << 8) | data[32];
+	z = (data[35] << 8) | data[34];
+	input_report_abs(drc->accel_input_dev, ABS_THROTTLE, (int16_t)x);
+	input_report_abs(drc->accel_input_dev, ABS_RUDDER, (int16_t)y);
+	input_report_abs(drc->accel_input_dev, ABS_WHEEL, (int16_t)z);
+	input_sync(drc->accel_input_dev);
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -239,6 +274,41 @@ static bool drc_setup_touch(struct drc *drc,
 	return true;
 }
 
+static bool drc_setup_accel(struct drc *drc,
+		struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " Accelerometer");
+	if (!input_dev)
+		return false;
+
+	input_dev->evbit[0] = BIT(EV_ABS);
+
+	/* 1G accel is reported as about -7600 */
+	input_set_abs_params(input_dev, ABS_X, ACCEL_MIN, ACCEL_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, ACCEL_MIN, ACCEL_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Z, ACCEL_MIN, ACCEL_MAX, 0, 0);
+
+	/* gyroscope */
+	input_set_abs_params(input_dev, ABS_RX, GYRO_MIN, GYRO_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RY, GYRO_MIN, GYRO_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RZ, GYRO_MIN, GYRO_MAX, 0, 0);
+
+	/* magnetometer */
+	/* TODO: Figure out which ABS_* would make more sense to expose, or
+	 * maybe go for the iio subsystem? */
+	input_set_abs_params(input_dev, ABS_THROTTLE, MAGNET_MIN, MAGNET_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RUDDER, MAGNET_MIN, MAGNET_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_WHEEL, MAGNET_MIN, MAGNET_MAX, 0, 0);
+
+	set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit);
+
+	drc->accel_input_dev = input_dev;
+
+	return true;
+}
+
 static bool drc_setup_joypad(struct drc *drc,
 		struct hid_device *hdev)
 {
@@ -303,13 +373,15 @@ static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	}
 
 	if (!drc_setup_joypad(drc, hdev) ||
-	    !drc_setup_touch(drc, hdev)) {
+	    !drc_setup_touch(drc, hdev) ||
+	    !drc_setup_accel(drc, hdev)) {
 		hid_err(hdev, "could not allocate interfaces\n");
 		return -ENOMEM;
 	}
 
 	ret = input_register_device(drc->joy_input_dev) ||
-		input_register_device(drc->touch_input_dev);
+		input_register_device(drc->touch_input_dev) ||
+		input_register_device(drc->accel_input_dev);
 	if (ret) {
 		hid_err(hdev, "failed to register interfaces\n");
 		return ret;
-- 
2.31.1


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

* [PATCH 4/4] HID: wiiu-drc: Add battery reporting
  2021-05-02 23:28 [PATCH 0/4] RFC: HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
                   ` (2 preceding siblings ...)
  2021-05-02 23:28 ` [PATCH 3/4] HID: wiiu-drc: Add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
@ 2021-05-02 23:28 ` Emmanuel Gil Peyrot
  2021-05-06 11:45   ` Barnabás Pőcze
  2021-05-19  8:59 ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
  4 siblings, 1 reply; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-02 23:28 UTC (permalink / raw)
  To: linux-input
  Cc: Emmanuel Gil Peyrot, Ash Logan, Jonathan Neuschäfer,
	Jiri Kosina, Benjamin Tissoires, linux-kernel

On my DRC the values only go between 142 (battery LED blinking red
before shutdown) and 178 (charge LED stopping), it seems to be the same
on other units according to other testers.

A spinlock is used to avoid the battery level and status from being
reported unsynchronised.

Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-wiiu-drc.c | 107 +++++++++++++++++++++++++++++++++++++
 1 file changed, 107 insertions(+)

diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
index 80faaaad2bb5..119d55542e31 100644
--- a/drivers/hid/hid-wiiu-drc.c
+++ b/drivers/hid/hid-wiiu-drc.c
@@ -15,6 +15,8 @@
 #include <linux/device.h>
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/spinlock.h>
 #include "hid-ids.h"
 
 #define DEVICE_NAME	"Nintendo Wii U gamepad"
@@ -69,6 +71,11 @@
 #define MAGNET_MIN	ACCEL_MIN
 #define MAGNET_MAX	ACCEL_MAX
 
+/* Battery constants */
+#define BATTERY_MIN	142
+#define BATTERY_MAX	178
+#define BATTERY_CAPACITY(val) ((val - BATTERY_MIN) * 100 / (BATTERY_MAX - BATTERY_MIN))
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
@@ -77,10 +84,17 @@
  */
 
 struct drc {
+	spinlock_t lock;
+
 	struct input_dev *joy_input_dev;
 	struct input_dev *touch_input_dev;
 	struct input_dev *accel_input_dev;
 	struct hid_device *hdev;
+	struct power_supply *battery;
+	struct power_supply_desc battery_desc;
+
+	u8 battery_energy;
+	int battery_status;
 };
 
 static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
@@ -89,6 +103,7 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 	struct drc *drc = hid_get_drvdata(hdev);
 	int i, x, y, z, pressure, base;
 	u32 buttons;
+	unsigned long flags;
 
 	if (len != 128)
 		return 0;
@@ -206,6 +221,17 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 	input_report_abs(drc->accel_input_dev, ABS_WHEEL, (int16_t)z);
 	input_sync(drc->accel_input_dev);
 
+	/* battery */
+	spin_lock_irqsave(&drc->lock, flags);
+	drc->battery_energy = data[5];
+	if (drc->battery_energy == BATTERY_MAX)
+		drc->battery_status = POWER_SUPPLY_STATUS_FULL;
+	else if ((data[4] & 0x40) != 0)
+		drc->battery_status = POWER_SUPPLY_STATUS_CHARGING;
+	else
+		drc->battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+	spin_unlock_irqrestore(&drc->lock, flags);
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -309,10 +335,67 @@ static bool drc_setup_accel(struct drc *drc,
 	return true;
 }
 
+static enum power_supply_property drc_battery_props[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_SCOPE,
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_ENERGY_NOW,
+	POWER_SUPPLY_PROP_ENERGY_EMPTY,
+	POWER_SUPPLY_PROP_ENERGY_FULL,
+};
+
+static int drc_battery_get_property(struct power_supply *psy,
+				    enum power_supply_property psp,
+				    union power_supply_propval *val)
+{
+	struct drc *drc = power_supply_get_drvdata(psy);
+	unsigned long flags;
+	int ret = 0;
+	u8 battery_energy;
+	int battery_status;
+
+	spin_lock_irqsave(&drc->lock, flags);
+	battery_energy = drc->battery_energy;
+	battery_status = drc->battery_status;
+	spin_unlock_irqrestore(&drc->lock, flags);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = BATTERY_CAPACITY(battery_energy);
+		break;
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = battery_status;
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_NOW:
+		val->intval = battery_energy;
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_EMPTY:
+		val->intval = BATTERY_MIN;
+		break;
+	case POWER_SUPPLY_PROP_ENERGY_FULL:
+		val->intval = BATTERY_MAX;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
 static bool drc_setup_joypad(struct drc *drc,
 		struct hid_device *hdev)
 {
 	struct input_dev *input_dev;
+	struct power_supply_config psy_cfg = { .drv_data = drc, };
+	int ret;
+	static uint8_t drc_num = 0;
 
 	input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad");
 	if (!input_dev)
@@ -350,6 +433,30 @@ static bool drc_setup_joypad(struct drc *drc,
 
 	drc->joy_input_dev = input_dev;
 
+	drc->battery_desc.properties = drc_battery_props;
+	drc->battery_desc.num_properties = ARRAY_SIZE(drc_battery_props);
+	drc->battery_desc.get_property = drc_battery_get_property;
+	drc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+	drc->battery_desc.use_for_apm = 0;
+
+	/*
+	 * TODO: It might be better to use the interface number as the drc_num,
+	 * but I don’t know how to fetch it from here.  In userland it is
+	 * /sys/devices/platform/latte/d140000.usb/usb3/3-1/3-1:1.?/bInterfaceNumber
+	 */
+	drc->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "wiiu-drc-%i-battery", drc_num++);
+	if (!drc->battery_desc.name)
+		return -ENOMEM;
+
+	drc->battery = devm_power_supply_register(&hdev->dev, &drc->battery_desc, &psy_cfg);
+	if (IS_ERR(drc->battery)) {
+		ret = PTR_ERR(drc->battery);
+		hid_err(hdev, "Unable to register battery device\n");
+		return ret;
+	}
+
+	power_supply_powers(drc->battery, &hdev->dev);
+
 	return true;
 }
 
-- 
2.31.1


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

* Re: [PATCH 1/4] HID: wiiu-drc: Add a driver for this gamepad
  2021-05-02 23:28 ` [PATCH 1/4] HID: wiiu-drc: Add a driver for this gamepad Emmanuel Gil Peyrot
@ 2021-05-05 22:33   ` Jonathan Neuschäfer
  2021-05-06 10:07     ` Emmanuel Gil Peyrot
  2021-05-06 11:53   ` Barnabás Pőcze
  1 sibling, 1 reply; 41+ messages in thread
From: Jonathan Neuschäfer @ 2021-05-05 22:33 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot
  Cc: linux-input, Ash Logan, Jonathan Neuschäfer, Jiri Kosina,
	Benjamin Tissoires, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3600 bytes --]

Hi,

some mostly trivial remarks and questions of curiosity below, because
I'm not very qualified to review the input subsystem side of things.


On Mon, May 03, 2021 at 01:28:32AM +0200, Emmanuel Gil Peyrot wrote:
> From: Ash Logan <ash@heyquark.com>
> 
> This driver is for the DRC (wireless gamepad) when plugged to the DRH of
> the Wii U, a chip exposing it as a USB device.

s/plugged/wirelessly connected/, rather

> 
> This first patch exposes the buttons and sticks of this device, so that
> it can act as a plain game controller.
> 
> Signed-off-by: Ash Logan <ash@heyquark.com>
> Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
> ---

Out of curiosity:

Do the HID reports travel over the wireless link from DRC to DRH, or are
they formed in DRH firmware?

Is there a reference of the device-specific HID format? I briefly looked
at https://libdrc.org/docs/index.html but couldn't find it there.


>  drivers/hid/Kconfig        |   7 +
>  drivers/hid/Makefile       |   1 +
>  drivers/hid/hid-ids.h      |   1 +
>  drivers/hid/hid-quirks.c   |   3 +
>  drivers/hid/hid-wiiu-drc.c | 270 +++++++++++++++++++++++++++++++++++++
>  5 files changed, 282 insertions(+)
>  create mode 100644 drivers/hid/hid-wiiu-drc.c
> 
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 4bf263c2d61a..01116c315459 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -1105,6 +1105,13 @@ config HID_WACOM
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called wacom.
>  
> +config HID_WIIU_DRC
> +	tristate "Nintendo Wii U gamepad over internal DRH"

                                 gamepad (DRC)

... so it's clearer where the "DRC" name comes from.

> +#if IS_ENABLED(CONFIG_HID_WIIU_DRC)
> +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
> +#endif

Is the DRC connection the only USB function that the DRH provides?


> +++ b/drivers/hid/hid-wiiu-drc.c
> @@ -0,0 +1,270 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * HID driver for Nintendo Wii U gamepad, connected via console-internal DRH

                                    gamepad (DRC)


> +static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
> +	 u8 *data, int len)
> +{
> +	struct drc *drc = hid_get_drvdata(hdev);
> +	int i;
> +	u32 buttons;
> +
> +	if (len != 128)
> +		return 0;

From include/linux/hid.h:

 * raw_event and event should return negative on error, any other value will
 * pass the event on to .event() typically return 0 for success.

Not sure if returning 0 as you do above is appropriate.


> +static bool drc_setup_joypad(struct drc *drc,
> +		struct hid_device *hdev)
> +{
> +	struct input_dev *input_dev;
> +
> +	input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad");

"Nintendo Wii U gamepad Joypad" looks a bit sub-optimal, but I'm not
sure about the conventions here.


> +
> +	/* These two buttons are actually TV control and Power. */
> +	set_bit(BTN_Z, input_dev->keybit);
> +	set_bit(BTN_DEAD, input_dev->keybit);

Hmm... from what I've deen the TV control button opens a menu on the
gamepad itself. Does it send the input event in addition to that?
Or is there a mode where it opens the TV menu, and a mode where it
forwards the button press to the Wii U?


> +MODULE_AUTHOR("Ash Logan <ash@heyquark.com>");

Since you're submitting the driver, rather than Ash, maybe adjust the
author field here? (totally your choice.)



Thanks,
Jonathan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH 2/4] HID: wiiu-drc: Implement touch reports
  2021-05-02 23:28 ` [PATCH 2/4] HID: wiiu-drc: Implement touch reports Emmanuel Gil Peyrot
@ 2021-05-05 22:43   ` Jonathan Neuschäfer
  2021-05-06 10:20     ` Emmanuel Gil Peyrot
  0 siblings, 1 reply; 41+ messages in thread
From: Jonathan Neuschäfer @ 2021-05-05 22:43 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot
  Cc: linux-input, Ash Logan, Jonathan Neuschäfer, Jiri Kosina,
	Benjamin Tissoires, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2020 bytes --]

Hi,

some more comments below. Enjoy :)

On Mon, May 03, 2021 at 01:28:33AM +0200, Emmanuel Gil Peyrot wrote:
> There is a 100×200 inaccessible border on each side, and the Y axis is
> inverted, these are the two main quirks of this touch panel.

Does that mean 100 px borders left and right, and 200 px borders top and
bottom?

100×200 evokes the image of a rectangle of that size, which I found
confusing for a moment.

> 
> I’ve been testing with weston-simple-touch mostly, but it also with the
> rest of Weston.
> 
> Signed-off-by: Ash Logan <ash@heyquark.com>
> Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
> ---
>  drivers/hid/hid-wiiu-drc.c | 83 +++++++++++++++++++++++++++++++++++---
>  1 file changed, 78 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
> index 018cbdb53a2c..77e70827c37d 100644
> --- a/drivers/hid/hid-wiiu-drc.c
> +++ b/drivers/hid/hid-wiiu-drc.c
> @@ -49,13 +49,27 @@
>  
>  #define BUTTON_POWER	BIT(25)
>  
> +/* Touch constants */
> +/* Resolution in pixels */
> +#define RES_X		854
> +#define RES_Y		480
> +/* Display/touch size in mm */
> +#define WIDTH		138
> +#define HEIGHT		79
> +#define NUM_TOUCH_POINTS 10
> +#define MAX_TOUCH_RES	(1 << 12)
> +#define TOUCH_BORDER_X	100
> +#define TOUCH_BORDER_Y	200

[...]
> +	/* touch */
> +	/* Average touch points for improved accuracy. */
> +	x = y = 0;
> +	for (i = 0; i < NUM_TOUCH_POINTS; i++) {
> +		base = 36 + 4 * i;
> +
> +		x += ((data[base + 1] & 0xF) << 8) | data[base];
> +		y += ((data[base + 3] & 0xF) << 8) | data[base + 2];
> +	}
> +	x /= NUM_TOUCH_POINTS;
> +	y /= NUM_TOUCH_POINTS;

Given that there are 10 possible touch points: Does the gamepad actually
support multitouch (usefully)?

If so, I think it would be better to report all touch points
individually to userspace, to allow for multitouch gestures;
userspace can still implement averaging if desired.



Thanks,
Jonathan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH 1/4] HID: wiiu-drc: Add a driver for this gamepad
  2021-05-05 22:33   ` Jonathan Neuschäfer
@ 2021-05-06 10:07     ` Emmanuel Gil Peyrot
  2021-05-06 10:29       ` Jonathan Neuschäfer
  0 siblings, 1 reply; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-06 10:07 UTC (permalink / raw)
  To: Jonathan Neuschäfer
  Cc: linux-input, Ash Logan, Jiri Kosina, Benjamin Tissoires, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 5077 bytes --]

On Wed, May 05, 2021 at 10:33:15PM +0000, Jonathan Neuschäfer wrote:
> Hi,

Hi,

> 
> some mostly trivial remarks and questions of curiosity below, because
> I'm not very qualified to review the input subsystem side of things.

Thanks for the questions anyway, I can probably make things clearer in
the patch thanks to them. :)

[…]
> Out of curiosity:
> 
> Do the HID reports travel over the wireless link from DRC to DRH, or are
> they formed in DRH firmware?

This HID report is a 1:1 copy of what the DRC sends, with no
modification that I could find.

> 
> Is there a reference of the device-specific HID format? I briefly looked
> at https://libdrc.org/docs/index.html but couldn't find it there.

You were very close, the input report is described here:
https://libdrc.org/docs/re/sc-input.html

This project wrote a userland driver for using the DRC without the DRH,
but it requires a very specific wifi chip which makes it quite
cumbersome to use.

> 
> 
> >  drivers/hid/Kconfig        |   7 +
> >  drivers/hid/Makefile       |   1 +
> >  drivers/hid/hid-ids.h      |   1 +
> >  drivers/hid/hid-quirks.c   |   3 +
> >  drivers/hid/hid-wiiu-drc.c | 270 +++++++++++++++++++++++++++++++++++++
> >  5 files changed, 282 insertions(+)
> >  create mode 100644 drivers/hid/hid-wiiu-drc.c
> > 
> > diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> > index 4bf263c2d61a..01116c315459 100644
> > --- a/drivers/hid/Kconfig
> > +++ b/drivers/hid/Kconfig
> > @@ -1105,6 +1105,13 @@ config HID_WACOM
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called wacom.
> >  
> > +config HID_WIIU_DRC
> > +	tristate "Nintendo Wii U gamepad over internal DRH"
> 
>                                  gamepad (DRC)
> 
> ... so it's clearer where the "DRC" name comes from.

Will do in v2.

> 
> > +#if IS_ENABLED(CONFIG_HID_WIIU_DRC)
> > +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
> > +#endif
> 
> Is the DRC connection the only USB function that the DRH provides?

As far as I know, yes.

But the DRC also sends microphone and camera data, which gets exposed by
the DRH, but juuuuuust not quite standard enough to work as is using
snd_usb_audio or uvcvideo.  There is also a NFC reader which no one has
reversed yet to my knowledge.

There are two DRCs exposed by the DRH, despite only one of them being
bundled with each Wii U, and no game ever making use of more.

> 
> 
> > +++ b/drivers/hid/hid-wiiu-drc.c
> > @@ -0,0 +1,270 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * HID driver for Nintendo Wii U gamepad, connected via console-internal DRH
> 
>                                     gamepad (DRC)

Ack, will be fixed in v2.

> 
> 
> > +static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
> > +	 u8 *data, int len)
> > +{
> > +	struct drc *drc = hid_get_drvdata(hdev);
> > +	int i;
> > +	u32 buttons;
> > +
> > +	if (len != 128)
> > +		return 0;
> 
> From include/linux/hid.h:
> 
>  * raw_event and event should return negative on error, any other value will
>  * pass the event on to .event() typically return 0 for success.
> 
> Not sure if returning 0 as you do above is appropriate.

Oops, thanks for noticing, this will be fixed in v2.

> 
> 
> > +static bool drc_setup_joypad(struct drc *drc,
> > +		struct hid_device *hdev)
> > +{
> > +	struct input_dev *input_dev;
> > +
> > +	input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad");
> 
> "Nintendo Wii U gamepad Joypad" looks a bit sub-optimal, but I'm not
> sure about the conventions here.

"Nintendo Wii U gamepad buttons and sticks" would be better I think.

> 
> 
> > +
> > +	/* These two buttons are actually TV control and Power. */
> > +	set_bit(BTN_Z, input_dev->keybit);
> > +	set_bit(BTN_DEAD, input_dev->keybit);
> 
> Hmm... from what I've deen the TV control button opens a menu on the
> gamepad itself. Does it send the input event in addition to that?
> Or is there a mode where it opens the TV menu, and a mode where it
> forwards the button press to the Wii U?

It does draw a line of text near the bottom of the screen, saying “TV
Remote can be configured in System Settings.”, but also sends the button
as a normal button in the report.  It could be possible to change its
behaviour (in System Settings perhaps?) but so far I’ve been avoiding
interacting with the proprietary OS.

The power button also has a special behaviour: when it is held for four
seconds, it will power off the DRC.

> 
> 
> > +MODULE_AUTHOR("Ash Logan <ash@heyquark.com>");
> 
> Since you're submitting the driver, rather than Ash, maybe adjust the
> author field here? (totally your choice.)

I’ll ask them, I’m perfectly fine with becoming the author, but they
wrote most of that code, I only fixed the last few missing pieces and
did some cleanup.

> 
> 
> 
> Thanks,
> Jonathan

Thanks!

-- 
Emmanuel Gil Peyrot

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH 2/4] HID: wiiu-drc: Implement touch reports
  2021-05-05 22:43   ` Jonathan Neuschäfer
@ 2021-05-06 10:20     ` Emmanuel Gil Peyrot
  0 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-06 10:20 UTC (permalink / raw)
  To: Jonathan Neuschäfer
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan, Jiri Kosina,
	Benjamin Tissoires, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2802 bytes --]

On Wed, May 05, 2021 at 10:43:55PM +0000, Jonathan Neuschäfer wrote:
> Hi,
> 
> some more comments below. Enjoy :)
> 
> On Mon, May 03, 2021 at 01:28:33AM +0200, Emmanuel Gil Peyrot wrote:
> > There is a 100×200 inaccessible border on each side, and the Y axis is
> > inverted, these are the two main quirks of this touch panel.
> 
> Does that mean 100 px borders left and right, and 200 px borders top and
> bottom?

Correct, I’ll reformulate in v2. :)

> 
> 100×200 evokes the image of a rectangle of that size, which I found
> confusing for a moment.
> 
> > 
> > I’ve been testing with weston-simple-touch mostly, but it also with the
> > rest of Weston.
> > 
> > Signed-off-by: Ash Logan <ash@heyquark.com>
> > Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
> > ---
> >  drivers/hid/hid-wiiu-drc.c | 83 +++++++++++++++++++++++++++++++++++---
> >  1 file changed, 78 insertions(+), 5 deletions(-)
> > 
> > diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
> > index 018cbdb53a2c..77e70827c37d 100644
> > --- a/drivers/hid/hid-wiiu-drc.c
> > +++ b/drivers/hid/hid-wiiu-drc.c
> > @@ -49,13 +49,27 @@
> >  
> >  #define BUTTON_POWER	BIT(25)
> >  
> > +/* Touch constants */
> > +/* Resolution in pixels */
> > +#define RES_X		854
> > +#define RES_Y		480
> > +/* Display/touch size in mm */
> > +#define WIDTH		138
> > +#define HEIGHT		79
> > +#define NUM_TOUCH_POINTS 10
> > +#define MAX_TOUCH_RES	(1 << 12)
> > +#define TOUCH_BORDER_X	100
> > +#define TOUCH_BORDER_Y	200
> 
> [...]
> > +	/* touch */
> > +	/* Average touch points for improved accuracy. */
> > +	x = y = 0;
> > +	for (i = 0; i < NUM_TOUCH_POINTS; i++) {
> > +		base = 36 + 4 * i;
> > +
> > +		x += ((data[base + 1] & 0xF) << 8) | data[base];
> > +		y += ((data[base + 3] & 0xF) << 8) | data[base + 2];
> > +	}
> > +	x /= NUM_TOUCH_POINTS;
> > +	y /= NUM_TOUCH_POINTS;
> 
> Given that there are 10 possible touch points: Does the gamepad actually
> support multitouch (usefully)?
> 
> If so, I think it would be better to report all touch points
> individually to userspace, to allow for multitouch gestures;
> userspace can still implement averaging if desired.

Sadly no, in my testing all ten reports are always within a few units
from each other, even if I press two (or more) different points on the
touchscreen at the same time.

My guess would be, the firmware and report format got written before
Nintendo decided whether to go for a capacitive or resistive touch
panel, and they didn’t get changed once the final decision was made to
go for a non-multitouch-aware resistive panel.

I’ll add some factual comment about this in v2.

> 
> 
> 
> Thanks,
> Jonathan

Thanks!

-- 
Emmanuel Gil Peyrot

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH 1/4] HID: wiiu-drc: Add a driver for this gamepad
  2021-05-06 10:07     ` Emmanuel Gil Peyrot
@ 2021-05-06 10:29       ` Jonathan Neuschäfer
  0 siblings, 0 replies; 41+ messages in thread
From: Jonathan Neuschäfer @ 2021-05-06 10:29 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot
  Cc: Jonathan Neuschäfer, linux-input, Ash Logan, Jiri Kosina,
	Benjamin Tissoires, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 700 bytes --]

On Thu, May 06, 2021 at 12:07:05PM +0200, Emmanuel Gil Peyrot wrote:
> On Wed, May 05, 2021 at 10:33:15PM +0000, Jonathan Neuschäfer wrote:
[...]
> > Is there a reference of the device-specific HID format? I briefly looked
> > at https://libdrc.org/docs/index.html but couldn't find it there.
> 
> You were very close, the input report is described here:
> https://libdrc.org/docs/re/sc-input.html

Ok, I think this link would be somewhat useful to have in the commit
message or a comment.

> This project wrote a userland driver for using the DRC without the DRH,
> but it requires a very specific wifi chip which makes it quite
> cumbersome to use.

I see.


Thanks,
Jonathan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH 4/4] HID: wiiu-drc: Add battery reporting
  2021-05-02 23:28 ` [PATCH 4/4] HID: wiiu-drc: Add battery reporting Emmanuel Gil Peyrot
@ 2021-05-06 11:45   ` Barnabás Pőcze
  0 siblings, 0 replies; 41+ messages in thread
From: Barnabás Pőcze @ 2021-05-06 11:45 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot
  Cc: linux-input, Ash Logan, Jonathan Neuschäfer, Jiri Kosina,
	Benjamin Tissoires, linux-kernel

Hi


2021. május 3., hétfő 1:28 keltezéssel, Emmanuel Gil Peyrot írta:

> On my DRC the values only go between 142 (battery LED blinking red
> before shutdown) and 178 (charge LED stopping), it seems to be the same
> on other units according to other testers.
>
> A spinlock is used to avoid the battery level and status from being
> reported unsynchronised.
>
> Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
> ---
>  drivers/hid/hid-wiiu-drc.c | 107 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 107 insertions(+)
>
> diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
> index 80faaaad2bb5..119d55542e31 100644
> --- a/drivers/hid/hid-wiiu-drc.c
> +++ b/drivers/hid/hid-wiiu-drc.c
> @@ -15,6 +15,8 @@
>  #include <linux/device.h>
>  #include <linux/hid.h>
>  #include <linux/module.h>
> +#include <linux/power_supply.h>
> +#include <linux/spinlock.h>
>  #include "hid-ids.h"
>
>  #define DEVICE_NAME	"Nintendo Wii U gamepad"
> @@ -69,6 +71,11 @@
>  #define MAGNET_MIN	ACCEL_MIN
>  #define MAGNET_MAX	ACCEL_MAX
>
> +/* Battery constants */
> +#define BATTERY_MIN	142
> +#define BATTERY_MAX	178
> +#define BATTERY_CAPACITY(val) ((val - BATTERY_MIN) * 100 / (BATTERY_MAX - BATTERY_MIN))

There's `fixp_linear_interpolate()` in linux/fixp-arithmetic.h,
you could use that.


> +
>  /*
>   * The device is setup with multiple input devices:
>   * - A joypad with the buttons and sticks.
> @@ -77,10 +84,17 @@
>   */
>
>  struct drc {
> +	spinlock_t lock;

I believe a `spin_lock_init()` call is missing from the code.


> +
>  	struct input_dev *joy_input_dev;
>  	struct input_dev *touch_input_dev;
>  	struct input_dev *accel_input_dev;
>  	struct hid_device *hdev;
> +	struct power_supply *battery;
> +	struct power_supply_desc battery_desc;
> +
> +	u8 battery_energy;
> +	int battery_status;
>  };
>
>  static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
> @@ -89,6 +103,7 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
>  	struct drc *drc = hid_get_drvdata(hdev);
>  	int i, x, y, z, pressure, base;
>  	u32 buttons;
> +	unsigned long flags;
>
>  	if (len != 128)
>  		return 0;
> @@ -206,6 +221,17 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
>  	input_report_abs(drc->accel_input_dev, ABS_WHEEL, (int16_t)z);
>  	input_sync(drc->accel_input_dev);
>
> +	/* battery */
> +	spin_lock_irqsave(&drc->lock, flags);
> +	drc->battery_energy = data[5];
> +	if (drc->battery_energy == BATTERY_MAX)
> +		drc->battery_status = POWER_SUPPLY_STATUS_FULL;
> +	else if ((data[4] & 0x40) != 0)

Maybe `if (data[4] & BIT(BATTERY_CHARGING))` or `if (data[4] & BATTERY_CHARGING_BIT)`
would be better.


> +		drc->battery_status = POWER_SUPPLY_STATUS_CHARGING;
> +	else
> +		drc->battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
> +	spin_unlock_irqrestore(&drc->lock, flags);
> +
>  	/* let hidraw and hiddev handle the report */
>  	return 0;
>  }
> @@ -309,10 +335,67 @@ static bool drc_setup_accel(struct drc *drc,
>  	return true;
>  }
>
> +static enum power_supply_property drc_battery_props[] = {
> +	POWER_SUPPLY_PROP_PRESENT,
> +	POWER_SUPPLY_PROP_CAPACITY,
> +	POWER_SUPPLY_PROP_SCOPE,
> +	POWER_SUPPLY_PROP_STATUS,
> +	POWER_SUPPLY_PROP_ENERGY_NOW,
> +	POWER_SUPPLY_PROP_ENERGY_EMPTY,
> +	POWER_SUPPLY_PROP_ENERGY_FULL,
> +};
> +
> +static int drc_battery_get_property(struct power_supply *psy,
> +				    enum power_supply_property psp,
> +				    union power_supply_propval *val)
> +{
> +	struct drc *drc = power_supply_get_drvdata(psy);
> +	unsigned long flags;
> +	int ret = 0;
> +	u8 battery_energy;
> +	int battery_status;
> +
> +	spin_lock_irqsave(&drc->lock, flags);
> +	battery_energy = drc->battery_energy;
> +	battery_status = drc->battery_status;
> +	spin_unlock_irqrestore(&drc->lock, flags);
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_PRESENT:
> +		val->intval = 1;
> +		break;
> +	case POWER_SUPPLY_PROP_SCOPE:
> +		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
> +		break;
> +	case POWER_SUPPLY_PROP_CAPACITY:
> +		val->intval = BATTERY_CAPACITY(battery_energy);
> +		break;
> +	case POWER_SUPPLY_PROP_STATUS:
> +		val->intval = battery_status;
> +		break;
> +	case POWER_SUPPLY_PROP_ENERGY_NOW:
> +		val->intval = battery_energy;
> +		break;
> +	case POWER_SUPPLY_PROP_ENERGY_EMPTY:
> +		val->intval = BATTERY_MIN;
> +		break;
> +	case POWER_SUPPLY_PROP_ENERGY_FULL:
> +		val->intval = BATTERY_MAX;
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +	return ret;
> +}
> +
>  static bool drc_setup_joypad(struct drc *drc,
>  		struct hid_device *hdev)
>  {
>  	struct input_dev *input_dev;
> +	struct power_supply_config psy_cfg = { .drv_data = drc, };
> +	int ret;
> +	static uint8_t drc_num = 0;

You probably need an atomic integer here and use `atomic_fetch_inc()`
in the `devm_kasprintf()` call.


>
>  	input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad");
>  	if (!input_dev)
> @@ -350,6 +433,30 @@ static bool drc_setup_joypad(struct drc *drc,
>
>  	drc->joy_input_dev = input_dev;
>
> +	drc->battery_desc.properties = drc_battery_props;
> +	drc->battery_desc.num_properties = ARRAY_SIZE(drc_battery_props);
> +	drc->battery_desc.get_property = drc_battery_get_property;
> +	drc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
> +	drc->battery_desc.use_for_apm = 0;
> +
> +	/*
> +	 * TODO: It might be better to use the interface number as the drc_num,
> +	 * but I don’t know how to fetch it from here.  In userland it is
> +	 * /sys/devices/platform/latte/d140000.usb/usb3/3-1/3-1:1.?/bInterfaceNumber
> +	 */

The interface number is not globally unique in any way as far as I can tell,
maybe the combination of the bus, port, device, interface numbers is.


> +	drc->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "wiiu-drc-%i-battery", drc_num++);
> +	if (!drc->battery_desc.name)
> +		return -ENOMEM;

The function returns `bool`. You might want to change it to `int` and return
a proper errno.


> +
> +	drc->battery = devm_power_supply_register(&hdev->dev, &drc->battery_desc, &psy_cfg);
> +	if (IS_ERR(drc->battery)) {
> +		ret = PTR_ERR(drc->battery);
> +		hid_err(hdev, "Unable to register battery device\n");
> +		return ret;
> +	}
> +
> +	power_supply_powers(drc->battery, &hdev->dev);
> +
>  	return true;
>  }
>
> --
> 2.31.1


Regards,
Barnabás Pőcze

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

* Re: [PATCH 1/4] HID: wiiu-drc: Add a driver for this gamepad
  2021-05-02 23:28 ` [PATCH 1/4] HID: wiiu-drc: Add a driver for this gamepad Emmanuel Gil Peyrot
  2021-05-05 22:33   ` Jonathan Neuschäfer
@ 2021-05-06 11:53   ` Barnabás Pőcze
  1 sibling, 0 replies; 41+ messages in thread
From: Barnabás Pőcze @ 2021-05-06 11:53 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot
  Cc: linux-input, Ash Logan, Jonathan Neuschäfer, Jiri Kosina,
	Benjamin Tissoires, linux-kernel

Hi


2021. május 3., hétfő 1:28 keltezéssel, Emmanuel Gil Peyrot írta:

> From: Ash Logan <ash@heyquark.com>
>
> This driver is for the DRC (wireless gamepad) when plugged to the DRH of
> the Wii U, a chip exposing it as a USB device.
>
> This first patch exposes the buttons and sticks of this device, so that
> it can act as a plain game controller.
>
> Signed-off-by: Ash Logan <ash@heyquark.com>
> Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
> ---
>  drivers/hid/Kconfig        |   7 +
>  drivers/hid/Makefile       |   1 +
>  drivers/hid/hid-ids.h      |   1 +
>  drivers/hid/hid-quirks.c   |   3 +
>  drivers/hid/hid-wiiu-drc.c | 270 +++++++++++++++++++++++++++++++++++++
>  5 files changed, 282 insertions(+)
>  create mode 100644 drivers/hid/hid-wiiu-drc.c
>
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 4bf263c2d61a..01116c315459 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -1105,6 +1105,13 @@ config HID_WACOM
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called wacom.
>
> +config HID_WIIU_DRC
> +	tristate "Nintendo Wii U gamepad over internal DRH"
> +	depends on HID
> +	help
> +	  Support for the Wii U gamepad, when connected with the Wii U’s
> +	  internal DRH chip.
> +
>  config HID_WIIMOTE
>  	tristate "Nintendo Wii / Wii U peripherals"
>  	depends on HID
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index 193431ec4db8..8fcaaeae4d65 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -134,6 +134,7 @@ wacom-objs			:= wacom_wac.o wacom_sys.o
>  obj-$(CONFIG_HID_WACOM)		+= wacom.o
>  obj-$(CONFIG_HID_WALTOP)	+= hid-waltop.o
>  obj-$(CONFIG_HID_WIIMOTE)	+= hid-wiimote.o
> +obj-$(CONFIG_HID_WIIU_DRC)	+= hid-wiiu-drc.o
>  obj-$(CONFIG_HID_SENSOR_HUB)	+= hid-sensor-hub.o
>  obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR)	+= hid-sensor-custom.o
>
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 84b8da3e7d09..fbac0dd021f1 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -916,6 +916,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_WIIU_DRH	0x0341
>
>  #define USB_VENDOR_ID_NOVATEK		0x0603
>  #define USB_DEVICE_ID_NOVATEK_PCT	0x0600
> diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
> index 3dd6f15f2a67..af400177537e 100644
> --- a/drivers/hid/hid-quirks.c
> +++ b/drivers/hid/hid-quirks.c
> @@ -513,6 +513,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
>  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
>  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
>  #endif
> +#if IS_ENABLED(CONFIG_HID_WIIU_DRC)
> +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
> +#endif
>  #if IS_ENABLED(CONFIG_HID_NTI)
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_NTI, USB_DEVICE_ID_USB_SUN) },
>  #endif
> diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
> new file mode 100644
> index 000000000000..018cbdb53a2c
> --- /dev/null
> +++ b/drivers/hid/hid-wiiu-drc.c
> @@ -0,0 +1,270 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * HID driver for Nintendo Wii U gamepad, connected via console-internal DRH
> + *
> + * Copyright (C) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
> + * Copyright (C) 2019 Ash Logan <ash@heyquark.com>
> + * Copyright (C) 2013 Mema Hacking
> + *
> + * Based on the excellent work at http://libdrc.org/docs/re/sc-input.html and
> + * https://bitbucket.org/memahaxx/libdrc/src/master/src/input-receiver.cpp .
> + * libdrc code is licensed under BSD 2-Clause.
> + * Driver based on hid-udraw-ps3.c.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +#include <linux/module.h>
> +#include "hid-ids.h"

It's usually best if you don't rely indirect includes. Include everything you use.
E.g. here linux/input.h is missing.


> +
> +#define DEVICE_NAME	"Nintendo Wii U gamepad"
> +
> +/* Button and stick constants */
> +#define VOLUME_MIN	0
> +#define VOLUME_MAX	255
> +#define NUM_STICK_AXES	4
> +#define STICK_MIN	900
> +#define STICK_MAX	3200
> +
> +#define BUTTON_SYNC	BIT(0)
> +#define BUTTON_HOME	BIT(1)
> +#define BUTTON_MINUS	BIT(2)
> +#define BUTTON_PLUS	BIT(3)
> +#define BUTTON_R	BIT(4)
> +#define BUTTON_L	BIT(5)
> +#define BUTTON_ZR	BIT(6)
> +#define BUTTON_ZL	BIT(7)
> +#define BUTTON_DOWN	BIT(8)
> +#define BUTTON_UP	BIT(9)
> +#define BUTTON_RIGHT	BIT(10)
> +#define BUTTON_LEFT	BIT(11)
> +#define BUTTON_Y	BIT(12)
> +#define BUTTON_X	BIT(13)
> +#define BUTTON_B	BIT(14)
> +#define BUTTON_A	BIT(15)
> +
> +#define BUTTON_TV	BIT(21)
> +#define BUTTON_R3	BIT(22)
> +#define BUTTON_L3	BIT(23)
> +
> +#define BUTTON_POWER	BIT(25)
> +
> +/*
> + * The device is setup with multiple input devices:
> + * - A joypad with the buttons and sticks.
> + */
> +
> +struct drc {
> +	struct input_dev *joy_input_dev;
> +	struct hid_device *hdev;
> +};
> +
> +static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
> +	 u8 *data, int len)
> +{
> +	struct drc *drc = hid_get_drvdata(hdev);
> +	int i;
> +	u32 buttons;
> +
> +	if (len != 128)
> +		return 0;
> +
> +	buttons = (data[4] << 24) | (data[80] << 16) | (data[2] << 8) | data[3];
> +	/* joypad */
> +	input_report_key(drc->joy_input_dev, BTN_DPAD_RIGHT, buttons & BUTTON_RIGHT);
> +	input_report_key(drc->joy_input_dev, BTN_DPAD_DOWN, buttons & BUTTON_DOWN);
> +	input_report_key(drc->joy_input_dev, BTN_DPAD_LEFT, buttons & BUTTON_LEFT);
> +	input_report_key(drc->joy_input_dev, BTN_DPAD_UP, buttons & BUTTON_UP);
> +
> +	input_report_key(drc->joy_input_dev, BTN_EAST, buttons & BUTTON_A);
> +	input_report_key(drc->joy_input_dev, BTN_SOUTH, buttons & BUTTON_B);
> +	input_report_key(drc->joy_input_dev, BTN_NORTH, buttons & BUTTON_X);
> +	input_report_key(drc->joy_input_dev, BTN_WEST, buttons & BUTTON_Y);
> +
> +	input_report_key(drc->joy_input_dev, BTN_TL, buttons & BUTTON_L);
> +	input_report_key(drc->joy_input_dev, BTN_TL2, buttons & BUTTON_ZL);
> +	input_report_key(drc->joy_input_dev, BTN_TR, buttons & BUTTON_R);
> +	input_report_key(drc->joy_input_dev, BTN_TR2, buttons & BUTTON_ZR);
> +
> +	input_report_key(drc->joy_input_dev, BTN_Z, buttons & BUTTON_TV);
> +	input_report_key(drc->joy_input_dev, BTN_THUMBL, buttons & BUTTON_L3);
> +	input_report_key(drc->joy_input_dev, BTN_THUMBR, buttons & BUTTON_R3);
> +
> +	input_report_key(drc->joy_input_dev, BTN_SELECT, buttons & BUTTON_MINUS);
> +	input_report_key(drc->joy_input_dev, BTN_START, buttons & BUTTON_PLUS);
> +	input_report_key(drc->joy_input_dev, BTN_MODE, buttons & BUTTON_HOME);
> +
> +	input_report_key(drc->joy_input_dev, BTN_DEAD, buttons & BUTTON_POWER);
> +
> +	for (i = 0; i < NUM_STICK_AXES; i++) {
> +		s16 val = (data[7 + 2*i] << 8) | data[6 + 2*i];
> +		/* clamp */
> +		if (val < STICK_MIN)
> +			val = STICK_MIN;
> +		if (val > STICK_MAX)
> +			val = STICK_MAX;

There's `clamp()` in linux/minmax.h, you might want to use that.


> +
> +		switch (i) {
> +		case 0:
> +			input_report_abs(drc->joy_input_dev, ABS_X, val);
> +			break;
> +		case 1:
> +			input_report_abs(drc->joy_input_dev, ABS_Y, val);
> +			break;
> +		case 2:
> +			input_report_abs(drc->joy_input_dev, ABS_RX, val);
> +			break;
> +		case 3:
> +			input_report_abs(drc->joy_input_dev, ABS_RY, val);
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	input_report_abs(drc->joy_input_dev, ABS_VOLUME, data[14]);
> +
> +	input_sync(drc->joy_input_dev);
> +
> +	/* let hidraw and hiddev handle the report */
> +	return 0;
> +}
> +
> +static int drc_open(struct input_dev *dev)
> +{
> +	struct drc *drc = input_get_drvdata(dev);
> +
> +	return hid_hw_open(drc->hdev);
> +}
> +
> +static void drc_close(struct input_dev *dev)
> +{
> +	struct drc *drc = input_get_drvdata(dev);
> +
> +	hid_hw_close(drc->hdev);
> +}
> +
> +static struct input_dev *allocate_and_setup(struct hid_device *hdev,
> +		const char *name)
> +{
> +	struct input_dev *input_dev;
> +
> +	input_dev = devm_input_allocate_device(&hdev->dev);
> +	if (!input_dev)
> +		return NULL;
> +
> +	input_dev->name = name;
> +	input_dev->phys = hdev->phys;
> +	input_dev->dev.parent = &hdev->dev;
> +	input_dev->open = drc_open;
> +	input_dev->close = drc_close;
> +	input_dev->uniq = hdev->uniq;
> +	input_dev->id.bustype = hdev->bus;
> +	input_dev->id.vendor  = hdev->vendor;
> +	input_dev->id.product = hdev->product;
> +	input_dev->id.version = hdev->version;
> +	input_set_drvdata(input_dev, hid_get_drvdata(hdev));
> +
> +	return input_dev;
> +}
> +
> +static bool drc_setup_joypad(struct drc *drc,
> +		struct hid_device *hdev)
> +{
> +	struct input_dev *input_dev;
> +
> +	input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad");
> +	if (!input_dev)
> +		return false;
> +
> +	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);

`input_set_abs_params()` already sets EV_ABS.


> +
> +	set_bit(BTN_DPAD_RIGHT, input_dev->keybit);
> +	set_bit(BTN_DPAD_DOWN, input_dev->keybit);
> +	set_bit(BTN_DPAD_LEFT, input_dev->keybit);
> +	set_bit(BTN_DPAD_UP, input_dev->keybit);
> +	set_bit(BTN_EAST, input_dev->keybit);
> +	set_bit(BTN_SOUTH, input_dev->keybit);
> +	set_bit(BTN_NORTH, input_dev->keybit);
> +	set_bit(BTN_WEST, input_dev->keybit);
> +	set_bit(BTN_TL, input_dev->keybit);
> +	set_bit(BTN_TL2, input_dev->keybit);
> +	set_bit(BTN_TR, input_dev->keybit);
> +	set_bit(BTN_TR2, input_dev->keybit);
> +	set_bit(BTN_THUMBL, input_dev->keybit);
> +	set_bit(BTN_THUMBR, input_dev->keybit);
> +	set_bit(BTN_SELECT, input_dev->keybit);
> +	set_bit(BTN_START, input_dev->keybit);
> +	set_bit(BTN_MODE, input_dev->keybit);
> +
> +	/* These two buttons are actually TV control and Power. */
> +	set_bit(BTN_Z, input_dev->keybit);
> +	set_bit(BTN_DEAD, input_dev->keybit);

You could use `input_set_capability(device, EV_KEY, ...)` (potentially in a loop)
instead of manually setting the bits. And then `input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);`
would be unnecessary.


> +
> +	input_set_abs_params(input_dev, ABS_X, STICK_MIN, STICK_MAX, 0, 0);
> +	input_set_abs_params(input_dev, ABS_Y, STICK_MIN, STICK_MAX, 0, 0);
> +	input_set_abs_params(input_dev, ABS_RX, STICK_MIN, STICK_MAX, 0, 0);
> +	input_set_abs_params(input_dev, ABS_RY, STICK_MIN, STICK_MAX, 0, 0);
> +	input_set_abs_params(input_dev, ABS_VOLUME, VOLUME_MIN, VOLUME_MAX, 0, 0);
> +
> +	drc->joy_input_dev = input_dev;
> +
> +	return true;
> +}
> +
> +static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
> +{
> +	struct drc *drc;
> +	int ret;
> +
> +	drc = devm_kzalloc(&hdev->dev, sizeof(struct drc), GFP_KERNEL);
> +	if (!drc)
> +		return -ENOMEM;
> +
> +	drc->hdev = hdev;
> +
> +	hid_set_drvdata(hdev, drc);
> +
> +	ret = hid_parse(hdev);
> +	if (ret) {
> +		hid_err(hdev, "parse failed\n");
> +		return ret;
> +	}
> +
> +	if (!drc_setup_joypad(drc, hdev)) {
> +		hid_err(hdev, "could not allocate interface\n");
> +		return -ENOMEM;
> +	}
> +
> +	ret = input_register_device(drc->joy_input_dev);
> +	if (ret) {
> +		hid_err(hdev, "failed to register interface\n");
> +		return ret;
> +	}
> +
> +	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER);

As far as I'm aware, `hid_hw_start()` should be called before `hid_hw_open()`.
Since you register the input device first, I think it is possible that `hid_hw_open()`
will be called first.


> +	if (ret) {
> +		hid_err(hdev, "hw start failed\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct hid_device_id drc_devices[] = {
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(hid, drc_devices);
> +
> +static struct hid_driver drc_driver = {
> +	.name = "hid-wiiu-drc",
> +	.id_table = drc_devices,
> +	.raw_event = drc_raw_event,
> +	.probe = drc_probe,
> +};
> +module_hid_driver(drc_driver);
> +
> +MODULE_AUTHOR("Ash Logan <ash@heyquark.com>");
> +MODULE_DESCRIPTION("Nintendo Wii U gamepad driver");
> +MODULE_LICENSE("GPL");
> --
> 2.31.1


Regards,
Barnabás Pőcze

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

* [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad
  2021-05-02 23:28 [PATCH 0/4] RFC: HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
                   ` (3 preceding siblings ...)
  2021-05-02 23:28 ` [PATCH 4/4] HID: wiiu-drc: Add battery reporting Emmanuel Gil Peyrot
@ 2021-05-19  8:59 ` Emmanuel Gil Peyrot
  2021-05-19  8:59   ` [PATCH v3 1/4] HID: wiiu-drc: Add a driver for this gamepad Emmanuel Gil Peyrot
                     ` (5 more replies)
  4 siblings, 6 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-19  8:59 UTC (permalink / raw)
  To: linux-input
  Cc: Emmanuel Gil Peyrot, Ash Logan, Jonathan Neuschäfer,
	Barnabás Pőcze, Jiri Kosina, Benjamin Tissoires,
	linux-kernel

This driver is for the DRC (wireless gamepad) when plugged to the DRH of
the Wii U, a chip exposing it as a USB device.

I tried to use this driver on master over usbip on my laptop, but usbip
disconnects the device right after the driver created the
/dev/input/event* files, so instead I have only tested this driver on
the 4.19 branch of the linux-wiiu[1] downstream.

Other than that, pretty much all of the HID parts of the gamepad work,
it’s only missing microphone, camera and NFC input now but those are
mostly standard (read require quirks) and pertain to other subsystems,
so I felt like this can be upstreamed already.

[1] https://gitlab.com/linux-wiiu/linux-wiiu

Changes since v1:
- Rename interfaces to be less redundant.
- Add comments for potentially unclear things.
- Reword some commits to include more needed information.
- Include all needed includes.
- Use helpful helper functions instead of (badly) reimplementing them
  myself.
- Always return the correct type for each function, to avoid some
  bool/int confusion, or returning 0 to mean error.
- Use myself as the module author, even though Ash did most of the
  initial work, I’m the one who will be maintaining this module from now
  on.
- Use input_set_capability() instead of set_bit(…, keybit) to also set
  BIT(EV_KEY) on evbit[0].
- Call hid_hw_start() before input_register_device() but after the setup
  functions, so that hid_hw_open() is never called before it.
- Add missing spin_lock_init() for the battery lock.
- Use a static atomic for the drc_num, and remove the comment about
  using the interface number.
- Interpret battery report as the voltage coming from an ADC, instead of
  the completely wrong ENERGY_NOW it was before.

So basically addressing Jonathan’s and Barnabás’ comments. :)

Changes since v2:
- Guard against the possibility of CONFIG_HID_BATTERY_STRENGTH not
  having been selected.
- Include forgotten linux/fixp-arith.h header.
- Fix a warning in clamp() due to comparing a s16 with a #define.

Ash Logan (1):
  HID: wiiu-drc: Add a driver for this gamepad

Emmanuel Gil Peyrot (3):
  HID: wiiu-drc: Implement touch reports
  HID: wiiu-drc: Add accelerometer, gyroscope and magnetometer readings
  HID: wiiu-drc: Add battery reporting

 drivers/hid/Kconfig        |   7 +
 drivers/hid/Makefile       |   1 +
 drivers/hid/hid-ids.h      |   1 +
 drivers/hid/hid-quirks.c   |   3 +
 drivers/hid/hid-wiiu-drc.c | 564 +++++++++++++++++++++++++++++++++++++
 5 files changed, 576 insertions(+)
 create mode 100644 drivers/hid/hid-wiiu-drc.c

-- 
2.31.1


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

* [PATCH v3 1/4] HID: wiiu-drc: Add a driver for this gamepad
  2021-05-19  8:59 ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
@ 2021-05-19  8:59   ` Emmanuel Gil Peyrot
  2021-05-19  8:59   ` [PATCH v3 2/4] HID: wiiu-drc: Implement touch reports Emmanuel Gil Peyrot
                     ` (4 subsequent siblings)
  5 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-19  8:59 UTC (permalink / raw)
  To: linux-input
  Cc: Ash Logan, Jonathan Neuschäfer, Barnabás Pőcze,
	Jiri Kosina, Benjamin Tissoires, linux-kernel,
	Emmanuel Gil Peyrot

From: Ash Logan <ash@heyquark.com>

This driver is for the DRC (gamepad) of the Wii U when wirelessly
connected to the DRH of the console, an internal chip exposing it as a
USB device.

This first patch exposes the buttons and sticks of this device, so that
it can act as a plain game controller.

The report format has been described by the libdrc folks at:
https://libdrc.org/docs/re/sc-input.html

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/Kconfig        |   7 +
 drivers/hid/Makefile       |   1 +
 drivers/hid/hid-ids.h      |   1 +
 drivers/hid/hid-quirks.c   |   3 +
 drivers/hid/hid-wiiu-drc.c | 281 +++++++++++++++++++++++++++++++++++++
 5 files changed, 293 insertions(+)
 create mode 100644 drivers/hid/hid-wiiu-drc.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 4bf263c2d61a..7681d4614c0a 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1105,6 +1105,13 @@ config HID_WACOM
 	  To compile this driver as a module, choose M here: the
 	  module will be called wacom.
 
+config HID_WIIU_DRC
+	tristate "Nintendo Wii U gamepad (DRC) over internal DRH"
+	depends on HID
+	help
+	  Support for the Wii U gamepad, when connected with the Wii U’s
+	  internal DRH chip.
+
 config HID_WIIMOTE
 	tristate "Nintendo Wii / Wii U peripherals"
 	depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 193431ec4db8..8fcaaeae4d65 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -134,6 +134,7 @@ wacom-objs			:= wacom_wac.o wacom_sys.o
 obj-$(CONFIG_HID_WACOM)		+= wacom.o
 obj-$(CONFIG_HID_WALTOP)	+= hid-waltop.o
 obj-$(CONFIG_HID_WIIMOTE)	+= hid-wiimote.o
+obj-$(CONFIG_HID_WIIU_DRC)	+= hid-wiiu-drc.o
 obj-$(CONFIG_HID_SENSOR_HUB)	+= hid-sensor-hub.o
 obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR)	+= hid-sensor-custom.o
 
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 84b8da3e7d09..fbac0dd021f1 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -916,6 +916,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_WIIU_DRH	0x0341
 
 #define USB_VENDOR_ID_NOVATEK		0x0603
 #define USB_DEVICE_ID_NOVATEK_PCT	0x0600
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 3dd6f15f2a67..af400177537e 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -513,6 +513,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
 #endif
+#if IS_ENABLED(CONFIG_HID_WIIU_DRC)
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
+#endif
 #if IS_ENABLED(CONFIG_HID_NTI)
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NTI, USB_DEVICE_ID_USB_SUN) },
 #endif
diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
new file mode 100644
index 000000000000..845908116c52
--- /dev/null
+++ b/drivers/hid/hid-wiiu-drc.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID driver for Nintendo Wii U gamepad (DRC), connected via console-internal DRH
+ *
+ * Copyright (C) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+ * Copyright (C) 2019 Ash Logan <ash@heyquark.com>
+ * Copyright (C) 2013 Mema Hacking
+ *
+ * Based on the excellent work at http://libdrc.org/docs/re/sc-input.html and
+ * https://bitbucket.org/memahaxx/libdrc/src/master/src/input-receiver.cpp .
+ * libdrc code is licensed under BSD 2-Clause.
+ * Driver based on hid-udraw-ps3.c.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include "hid-ids.h"
+
+#define DEVICE_NAME	"Nintendo Wii U gamepad (DRC)"
+
+/* Button and stick constants */
+#define VOLUME_MIN	0
+#define VOLUME_MAX	255
+#define NUM_STICK_AXES	4
+#define STICK_MIN	900
+#define STICK_MAX	3200
+
+#define BUTTON_SYNC	BIT(0)
+#define BUTTON_HOME	BIT(1)
+#define BUTTON_MINUS	BIT(2)
+#define BUTTON_PLUS	BIT(3)
+#define BUTTON_R	BIT(4)
+#define BUTTON_L	BIT(5)
+#define BUTTON_ZR	BIT(6)
+#define BUTTON_ZL	BIT(7)
+#define BUTTON_DOWN	BIT(8)
+#define BUTTON_UP	BIT(9)
+#define BUTTON_RIGHT	BIT(10)
+#define BUTTON_LEFT	BIT(11)
+#define BUTTON_Y	BIT(12)
+#define BUTTON_X	BIT(13)
+#define BUTTON_B	BIT(14)
+#define BUTTON_A	BIT(15)
+
+#define BUTTON_TV	BIT(21)
+#define BUTTON_R3	BIT(22)
+#define BUTTON_L3	BIT(23)
+
+#define BUTTON_POWER	BIT(25)
+
+/*
+ * The device is setup with multiple input devices:
+ * - A joypad with the buttons and sticks.
+ */
+
+struct drc {
+	struct input_dev *joy_input_dev;
+	struct hid_device *hdev;
+};
+
+/*
+ * The format of this report has been reversed by the libdrc project, the
+ * documentation can be found here:
+ * https://libdrc.org/docs/re/sc-input.html
+ *
+ * We receive this report from USB, but it is actually formed on the DRC, the
+ * DRH only retransmits it over USB.
+ */
+static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
+			 u8 *data, int len)
+{
+	struct drc *drc = hid_get_drvdata(hdev);
+	int i;
+	u32 buttons;
+
+	if (len != 128)
+		return -EINVAL;
+
+	buttons = (data[4] << 24) | (data[80] << 16) | (data[2] << 8) | data[3];
+	/* joypad */
+	input_report_key(drc->joy_input_dev, BTN_DPAD_RIGHT, buttons & BUTTON_RIGHT);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_DOWN, buttons & BUTTON_DOWN);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_LEFT, buttons & BUTTON_LEFT);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_UP, buttons & BUTTON_UP);
+
+	input_report_key(drc->joy_input_dev, BTN_EAST, buttons & BUTTON_A);
+	input_report_key(drc->joy_input_dev, BTN_SOUTH, buttons & BUTTON_B);
+	input_report_key(drc->joy_input_dev, BTN_NORTH, buttons & BUTTON_X);
+	input_report_key(drc->joy_input_dev, BTN_WEST, buttons & BUTTON_Y);
+
+	input_report_key(drc->joy_input_dev, BTN_TL, buttons & BUTTON_L);
+	input_report_key(drc->joy_input_dev, BTN_TL2, buttons & BUTTON_ZL);
+	input_report_key(drc->joy_input_dev, BTN_TR, buttons & BUTTON_R);
+	input_report_key(drc->joy_input_dev, BTN_TR2, buttons & BUTTON_ZR);
+
+	input_report_key(drc->joy_input_dev, BTN_Z, buttons & BUTTON_TV);
+	input_report_key(drc->joy_input_dev, BTN_THUMBL, buttons & BUTTON_L3);
+	input_report_key(drc->joy_input_dev, BTN_THUMBR, buttons & BUTTON_R3);
+
+	input_report_key(drc->joy_input_dev, BTN_SELECT, buttons & BUTTON_MINUS);
+	input_report_key(drc->joy_input_dev, BTN_START, buttons & BUTTON_PLUS);
+	input_report_key(drc->joy_input_dev, BTN_MODE, buttons & BUTTON_HOME);
+
+	input_report_key(drc->joy_input_dev, BTN_DEAD, buttons & BUTTON_POWER);
+
+	for (i = 0; i < NUM_STICK_AXES; i++) {
+		s16 val = (data[7 + 2*i] << 8) | data[6 + 2*i];
+
+		val = clamp(val, (s16)STICK_MIN, (s16)STICK_MAX);
+
+		switch (i) {
+		case 0:
+			input_report_abs(drc->joy_input_dev, ABS_X, val);
+			break;
+		case 1:
+			input_report_abs(drc->joy_input_dev, ABS_Y, val);
+			break;
+		case 2:
+			input_report_abs(drc->joy_input_dev, ABS_RX, val);
+			break;
+		case 3:
+			input_report_abs(drc->joy_input_dev, ABS_RY, val);
+			break;
+		default:
+			break;
+		}
+	}
+
+	input_report_abs(drc->joy_input_dev, ABS_VOLUME, data[14]);
+	input_sync(drc->joy_input_dev);
+
+	/* let hidraw and hiddev handle the report */
+	return 0;
+}
+
+static int drc_open(struct input_dev *dev)
+{
+	struct drc *drc = input_get_drvdata(dev);
+
+	return hid_hw_open(drc->hdev);
+}
+
+static void drc_close(struct input_dev *dev)
+{
+	struct drc *drc = input_get_drvdata(dev);
+
+	hid_hw_close(drc->hdev);
+}
+
+static struct input_dev *allocate_and_setup(struct hid_device *hdev,
+					    const char *name)
+{
+	struct input_dev *input_dev;
+
+	input_dev = devm_input_allocate_device(&hdev->dev);
+	if (!input_dev)
+		return NULL;
+
+	input_dev->name = name;
+	input_dev->phys = hdev->phys;
+	input_dev->dev.parent = &hdev->dev;
+	input_dev->open = drc_open;
+	input_dev->close = drc_close;
+	input_dev->uniq = hdev->uniq;
+	input_dev->id.bustype = hdev->bus;
+	input_dev->id.vendor  = hdev->vendor;
+	input_dev->id.product = hdev->product;
+	input_dev->id.version = hdev->version;
+	input_set_drvdata(input_dev, hid_get_drvdata(hdev));
+
+	return input_dev;
+}
+
+static bool drc_setup_joypad(struct drc *drc,
+			     struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " buttons and sticks");
+	if (!input_dev)
+		return false;
+
+	drc->joy_input_dev = input_dev;
+
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_RIGHT);
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_DOWN);
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_LEFT);
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_UP);
+	input_set_capability(input_dev, EV_KEY, BTN_EAST);
+	input_set_capability(input_dev, EV_KEY, BTN_SOUTH);
+	input_set_capability(input_dev, EV_KEY, BTN_NORTH);
+	input_set_capability(input_dev, EV_KEY, BTN_WEST);
+	input_set_capability(input_dev, EV_KEY, BTN_TL);
+	input_set_capability(input_dev, EV_KEY, BTN_TL2);
+	input_set_capability(input_dev, EV_KEY, BTN_TR);
+	input_set_capability(input_dev, EV_KEY, BTN_TR2);
+	input_set_capability(input_dev, EV_KEY, BTN_THUMBL);
+	input_set_capability(input_dev, EV_KEY, BTN_THUMBR);
+	input_set_capability(input_dev, EV_KEY, BTN_SELECT);
+	input_set_capability(input_dev, EV_KEY, BTN_START);
+	input_set_capability(input_dev, EV_KEY, BTN_MODE);
+
+	/*
+	 * These two buttons are actually TV Control and Power.
+	 *
+	 * TV Control draws a line at the bottom of the DRC’s screen saying to
+	 * go into System Settings (on the original proprietary OS), while
+	 * Power will shutdown the DRC when held for four seconds, but those
+	 * two are still normal buttons otherwise.
+	 */
+	input_set_capability(input_dev, EV_KEY, BTN_Z);
+	input_set_capability(input_dev, EV_KEY, BTN_DEAD);
+
+	input_set_abs_params(input_dev, ABS_X, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RX, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RY, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_VOLUME, VOLUME_MIN, VOLUME_MAX, 0, 0);
+
+	return true;
+}
+
+static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	struct drc *drc;
+	int ret;
+
+	drc = devm_kzalloc(&hdev->dev, sizeof(struct drc), GFP_KERNEL);
+	if (!drc)
+		return -ENOMEM;
+
+	drc->hdev = hdev;
+
+	hid_set_drvdata(hdev, drc);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "parse failed\n");
+		return ret;
+	}
+
+	if (!drc_setup_joypad(drc, hdev)) {
+		hid_err(hdev, "could not allocate interface\n");
+		return -ENOMEM;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER);
+	if (ret) {
+		hid_err(hdev, "hw start failed\n");
+		return ret;
+	}
+
+	ret = input_register_device(drc->joy_input_dev);
+	if (ret) {
+		hid_err(hdev, "failed to register interface\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct hid_device_id drc_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, drc_devices);
+
+static struct hid_driver drc_driver = {
+	.name = "hid-wiiu-drc",
+	.id_table = drc_devices,
+	.raw_event = drc_raw_event,
+	.probe = drc_probe,
+};
+module_hid_driver(drc_driver);
+
+MODULE_AUTHOR("Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>");
+MODULE_DESCRIPTION("Nintendo Wii U gamepad (DRC) driver");
+MODULE_LICENSE("GPL");
-- 
2.31.1


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

* [PATCH v3 2/4] HID: wiiu-drc: Implement touch reports
  2021-05-19  8:59 ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
  2021-05-19  8:59   ` [PATCH v3 1/4] HID: wiiu-drc: Add a driver for this gamepad Emmanuel Gil Peyrot
@ 2021-05-19  8:59   ` Emmanuel Gil Peyrot
  2021-05-19  8:59   ` [PATCH v3 3/4] HID: wiiu-drc: Add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
                     ` (3 subsequent siblings)
  5 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-19  8:59 UTC (permalink / raw)
  To: linux-input
  Cc: Emmanuel Gil Peyrot, Ash Logan, Jonathan Neuschäfer,
	Barnabás Pőcze, Jiri Kosina, Benjamin Tissoires,
	linux-kernel

There is an inaccessible border on each side, 100 units on the left and
right sides, and 200 units at the top and bottom.  The Y axis is
inverted too, these are the two main quirks of this touch panel.

I’ve been testing with weston-simple-touch mostly, but it also with the
rest of Weston and it aligns perfectly without the need of any
additional calibration.

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-wiiu-drc.c | 86 +++++++++++++++++++++++++++++++++++---
 1 file changed, 81 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
index 845908116c52..a5d347d5f662 100644
--- a/drivers/hid/hid-wiiu-drc.c
+++ b/drivers/hid/hid-wiiu-drc.c
@@ -51,13 +51,27 @@
 
 #define BUTTON_POWER	BIT(25)
 
+/* Touch constants */
+/* Resolution in pixels */
+#define RES_X		854
+#define RES_Y		480
+/* Display/touch size in mm */
+#define WIDTH		138
+#define HEIGHT		79
+#define NUM_TOUCH_POINTS 10
+#define MAX_TOUCH_RES	(1 << 12)
+#define TOUCH_BORDER_X	100
+#define TOUCH_BORDER_Y	200
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
+ * - The touch area which works as a touchscreen.
  */
 
 struct drc {
 	struct input_dev *joy_input_dev;
+	struct input_dev *touch_input_dev;
 	struct hid_device *hdev;
 };
 
@@ -73,7 +87,7 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 			 u8 *data, int len)
 {
 	struct drc *drc = hid_get_drvdata(hdev);
-	int i;
+	int i, x, y, pressure, base;
 	u32 buttons;
 
 	if (len != 128)
@@ -132,6 +146,42 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 	input_report_abs(drc->joy_input_dev, ABS_VOLUME, data[14]);
 	input_sync(drc->joy_input_dev);
 
+	/* touch */
+	/*
+	 * Average touch points for improved accuracy.  Sadly these are always
+	 * reported extremely close from each other…  Even when the user
+	 * pressed two (or more) different points, all ten values will be
+	 * approximately in the middle of the pressure points.
+	 */
+	x = y = 0;
+	for (i = 0; i < NUM_TOUCH_POINTS; i++) {
+		base = 36 + 4 * i;
+
+		x += ((data[base + 1] & 0xF) << 8) | data[base];
+		y += ((data[base + 3] & 0xF) << 8) | data[base + 2];
+	}
+	x /= NUM_TOUCH_POINTS;
+	y /= NUM_TOUCH_POINTS;
+
+	/* Pressure reporting isn’t properly understood, so we don’t report it yet. */
+	pressure = 0;
+	pressure |= ((data[37] >> 4) & 7) << 0;
+	pressure |= ((data[39] >> 4) & 7) << 3;
+	pressure |= ((data[41] >> 4) & 7) << 6;
+	pressure |= ((data[43] >> 4) & 7) << 9;
+
+	if (pressure != 0) {
+		input_report_key(drc->touch_input_dev, BTN_TOUCH, 1);
+		input_report_key(drc->touch_input_dev, BTN_TOOL_FINGER, 1);
+
+		input_report_abs(drc->touch_input_dev, ABS_X, x);
+		input_report_abs(drc->touch_input_dev, ABS_Y, MAX_TOUCH_RES - y);
+	} else {
+		input_report_key(drc->touch_input_dev, BTN_TOUCH, 0);
+		input_report_key(drc->touch_input_dev, BTN_TOOL_FINGER, 0);
+	}
+	input_sync(drc->touch_input_dev);
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -223,6 +273,30 @@ static bool drc_setup_joypad(struct drc *drc,
 	return true;
 }
 
+static bool drc_setup_touch(struct drc *drc,
+			    struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " touchscreen");
+	if (!input_dev)
+		return false;
+
+	drc->touch_input_dev = input_dev;
+
+	set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+	input_set_abs_params(input_dev, ABS_X, TOUCH_BORDER_X, MAX_TOUCH_RES - TOUCH_BORDER_X, 20, 0);
+	input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH);
+	input_set_abs_params(input_dev, ABS_Y, TOUCH_BORDER_Y, MAX_TOUCH_RES - TOUCH_BORDER_Y, 20, 0);
+	input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT);
+
+	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+	input_set_capability(input_dev, EV_KEY, BTN_TOOL_FINGER);
+
+	return true;
+}
+
 static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
 	struct drc *drc;
@@ -242,8 +316,9 @@ static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		return ret;
 	}
 
-	if (!drc_setup_joypad(drc, hdev)) {
-		hid_err(hdev, "could not allocate interface\n");
+	if (!drc_setup_joypad(drc, hdev) ||
+	    !drc_setup_touch(drc, hdev)) {
+		hid_err(hdev, "could not allocate interfaces\n");
 		return -ENOMEM;
 	}
 
@@ -253,9 +328,10 @@ static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		return ret;
 	}
 
-	ret = input_register_device(drc->joy_input_dev);
+	ret = input_register_device(drc->joy_input_dev) ||
+	      input_register_device(drc->touch_input_dev);
 	if (ret) {
-		hid_err(hdev, "failed to register interface\n");
+		hid_err(hdev, "failed to register interfaces\n");
 		return ret;
 	}
 
-- 
2.31.1


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

* [PATCH v3 3/4] HID: wiiu-drc: Add accelerometer, gyroscope and magnetometer readings
  2021-05-19  8:59 ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
  2021-05-19  8:59   ` [PATCH v3 1/4] HID: wiiu-drc: Add a driver for this gamepad Emmanuel Gil Peyrot
  2021-05-19  8:59   ` [PATCH v3 2/4] HID: wiiu-drc: Implement touch reports Emmanuel Gil Peyrot
@ 2021-05-19  8:59   ` Emmanuel Gil Peyrot
  2021-05-19  8:59   ` [PATCH v3 4/4] HID: wiiu-drc: Add battery reporting Emmanuel Gil Peyrot
                     ` (2 subsequent siblings)
  5 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-19  8:59 UTC (permalink / raw)
  To: linux-input
  Cc: Emmanuel Gil Peyrot, Ash Logan, Jonathan Neuschäfer,
	Barnabás Pőcze, Jiri Kosina, Benjamin Tissoires,
	linux-kernel

These are mostly untested so far, because I have no idea which userland
to test against, but evtest at least seems to give sensible values.

The magnetometer doesn’t have dedicated INPUT_PROP_ACCELEROMETER
buttons, so I used three clearly invalid absolute values, in the hope
that someone will fix that eventually.  Another solution might be to go
for the iio subsystem instead, but then it wouldn’t be tied to the HID
any longer and I would feel uneasy about that.  Especially because
multiple such gamepads could be connected to a single computer.

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-wiiu-drc.c | 77 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 74 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
index a5d347d5f662..0cf5b657fec9 100644
--- a/drivers/hid/hid-wiiu-drc.c
+++ b/drivers/hid/hid-wiiu-drc.c
@@ -63,15 +63,25 @@
 #define TOUCH_BORDER_X	100
 #define TOUCH_BORDER_Y	200
 
+/* Accelerometer, gyroscope and magnetometer constants */
+#define ACCEL_MIN	-(1 << 15)
+#define ACCEL_MAX	((1 << 15) - 1)
+#define GYRO_MIN	-(1 << 23)
+#define GYRO_MAX	((1 << 23) - 1)
+#define MAGNET_MIN	-(1 << 15)
+#define MAGNET_MAX	((1 << 15) - 1)
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
  * - The touch area which works as a touchscreen.
+ * - An accelerometer + gyroscope + magnetometer device.
  */
 
 struct drc {
 	struct input_dev *joy_input_dev;
 	struct input_dev *touch_input_dev;
+	struct input_dev *accel_input_dev;
 	struct hid_device *hdev;
 };
 
@@ -87,7 +97,7 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 			 u8 *data, int len)
 {
 	struct drc *drc = hid_get_drvdata(hdev);
-	int i, x, y, pressure, base;
+	int i, x, y, z, pressure, base;
 	u32 buttons;
 
 	if (len != 128)
@@ -182,6 +192,31 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 	}
 	input_sync(drc->touch_input_dev);
 
+	/* accelerometer */
+	x = (data[16] << 8) | data[15];
+	y = (data[18] << 8) | data[17];
+	z = (data[20] << 8) | data[19];
+	input_report_abs(drc->accel_input_dev, ABS_X, (int16_t)x);
+	input_report_abs(drc->accel_input_dev, ABS_Y, (int16_t)y);
+	input_report_abs(drc->accel_input_dev, ABS_Z, (int16_t)z);
+
+	/* gyroscope */
+	x = (data[23] << 24) | (data[22] << 16) | (data[21] << 8);
+	y = (data[26] << 24) | (data[25] << 16) | (data[24] << 8);
+	z = (data[29] << 24) | (data[28] << 16) | (data[27] << 8);
+	input_report_abs(drc->accel_input_dev, ABS_RX, x >> 8);
+	input_report_abs(drc->accel_input_dev, ABS_RY, y >> 8);
+	input_report_abs(drc->accel_input_dev, ABS_RZ, z >> 8);
+
+	/* magnetometer */
+	x = (data[31] << 8) | data[30];
+	y = (data[33] << 8) | data[32];
+	z = (data[35] << 8) | data[34];
+	input_report_abs(drc->accel_input_dev, ABS_THROTTLE, (int16_t)x);
+	input_report_abs(drc->accel_input_dev, ABS_RUDDER, (int16_t)y);
+	input_report_abs(drc->accel_input_dev, ABS_WHEEL, (int16_t)z);
+	input_sync(drc->accel_input_dev);
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -297,6 +332,40 @@ static bool drc_setup_touch(struct drc *drc,
 	return true;
 }
 
+static bool drc_setup_accel(struct drc *drc,
+			    struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " accelerometer, gyroscope and magnetometer");
+	if (!input_dev)
+		return false;
+
+	drc->accel_input_dev = input_dev;
+
+	set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit);
+
+	/* 1G accel is reported as about -7900 */
+	input_set_abs_params(input_dev, ABS_X, ACCEL_MIN, ACCEL_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, ACCEL_MIN, ACCEL_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Z, ACCEL_MIN, ACCEL_MAX, 0, 0);
+
+	/* gyroscope */
+	input_set_abs_params(input_dev, ABS_RX, GYRO_MIN, GYRO_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RY, GYRO_MIN, GYRO_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RZ, GYRO_MIN, GYRO_MAX, 0, 0);
+
+	/* magnetometer */
+	/* TODO: Figure out which ABS_* would make more sense to expose, or
+	 * maybe go for the iio subsystem?
+	 */
+	input_set_abs_params(input_dev, ABS_THROTTLE, MAGNET_MIN, MAGNET_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RUDDER, MAGNET_MIN, MAGNET_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_WHEEL, MAGNET_MIN, MAGNET_MAX, 0, 0);
+
+	return true;
+}
+
 static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
 	struct drc *drc;
@@ -317,7 +386,8 @@ static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	}
 
 	if (!drc_setup_joypad(drc, hdev) ||
-	    !drc_setup_touch(drc, hdev)) {
+	    !drc_setup_touch(drc, hdev) ||
+	    !drc_setup_accel(drc, hdev)) {
 		hid_err(hdev, "could not allocate interfaces\n");
 		return -ENOMEM;
 	}
@@ -329,7 +399,8 @@ static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	}
 
 	ret = input_register_device(drc->joy_input_dev) ||
-	      input_register_device(drc->touch_input_dev);
+	      input_register_device(drc->touch_input_dev) ||
+	      input_register_device(drc->accel_input_dev);
 	if (ret) {
 		hid_err(hdev, "failed to register interfaces\n");
 		return ret;
-- 
2.31.1


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

* [PATCH v3 4/4] HID: wiiu-drc: Add battery reporting
  2021-05-19  8:59 ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
                     ` (2 preceding siblings ...)
  2021-05-19  8:59   ` [PATCH v3 3/4] HID: wiiu-drc: Add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
@ 2021-05-19  8:59   ` Emmanuel Gil Peyrot
  2021-09-21 15:08   ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
  2021-10-19 11:04   ` [PATCH v4 0/5] HID: nintendo: Add support " Emmanuel Gil Peyrot
  5 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-05-19  8:59 UTC (permalink / raw)
  To: linux-input
  Cc: Emmanuel Gil Peyrot, Ash Logan, Jonathan Neuschäfer,
	Barnabás Pőcze, Jiri Kosina, Benjamin Tissoires,
	linux-kernel

On my DRC the values only go between 142 (battery LED blinking red
before shutdown) and 178 (charge LED stopping), it seems to be the same
on other units according to other testers.  This might be the raw
voltage value as reported by an ADC, so adding a linear interpolation
between two common battery voltage values.

A spinlock is used to avoid the battery level and status from being
reported unsynchronised.

Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-wiiu-drc.c | 136 +++++++++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/drivers/hid/hid-wiiu-drc.c b/drivers/hid/hid-wiiu-drc.c
index 0cf5b657fec9..6b1a2cc344d4 100644
--- a/drivers/hid/hid-wiiu-drc.c
+++ b/drivers/hid/hid-wiiu-drc.c
@@ -17,6 +17,11 @@
 #include <linux/input.h>
 #include <linux/minmax.h>
 #include <linux/module.h>
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+#include <linux/fixp-arith.h>
+#include <linux/power_supply.h>
+#include <linux/spinlock.h>
+#endif
 #include "hid-ids.h"
 
 #define DEVICE_NAME	"Nintendo Wii U gamepad (DRC)"
@@ -71,6 +76,13 @@
 #define MAGNET_MIN	-(1 << 15)
 #define MAGNET_MAX	((1 << 15) - 1)
 
+/* ADC constants for the battery */
+#define BATTERY_CHARGING_BIT	BIT(6)
+#define BATTERY_MIN	142
+#define BATTERY_MAX	178
+#define VOLTAGE_MIN	3270000
+#define VOLTAGE_MAX	4100000
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
@@ -83,6 +95,14 @@ struct drc {
 	struct input_dev *touch_input_dev;
 	struct input_dev *accel_input_dev;
 	struct hid_device *hdev;
+
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	struct power_supply *battery;
+	struct power_supply_desc battery_desc;
+	spinlock_t battery_lock;
+	u8 battery_energy;
+	int battery_status;
+#endif
 };
 
 /*
@@ -99,6 +119,9 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 	struct drc *drc = hid_get_drvdata(hdev);
 	int i, x, y, z, pressure, base;
 	u32 buttons;
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	unsigned long flags;
+#endif
 
 	if (len != 128)
 		return -EINVAL;
@@ -217,6 +240,19 @@ static int drc_raw_event(struct hid_device *hdev, struct hid_report *report,
 	input_report_abs(drc->accel_input_dev, ABS_WHEEL, (int16_t)z);
 	input_sync(drc->accel_input_dev);
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	/* battery */
+	spin_lock_irqsave(&drc->battery_lock, flags);
+	drc->battery_energy = data[5];
+	if (drc->battery_energy == BATTERY_MAX)
+		drc->battery_status = POWER_SUPPLY_STATUS_FULL;
+	else if (data[4] & BATTERY_CHARGING_BIT)
+		drc->battery_status = POWER_SUPPLY_STATUS_CHARGING;
+	else
+		drc->battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+	spin_unlock_irqrestore(&drc->battery_lock, flags);
+#endif
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -366,6 +402,98 @@ static bool drc_setup_accel(struct drc *drc,
 	return true;
 }
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+static enum power_supply_property drc_battery_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_SCOPE,
+};
+
+static int drc_battery_get_property(struct power_supply *psy,
+				    enum power_supply_property psp,
+				    union power_supply_propval *val)
+{
+	struct drc *drc = power_supply_get_drvdata(psy);
+	unsigned long flags;
+	int ret = 0;
+	u8 battery_energy;
+	int battery_status;
+
+	spin_lock_irqsave(&drc->battery_lock, flags);
+	battery_energy = drc->battery_energy;
+	battery_status = drc->battery_status;
+	spin_unlock_irqrestore(&drc->battery_lock, flags);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = battery_status;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		val->intval = VOLTAGE_MAX;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		val->intval = VOLTAGE_MIN;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = fixp_linear_interpolate(BATTERY_MIN, VOLTAGE_MIN,
+						      BATTERY_MAX, VOLTAGE_MAX,
+						      battery_energy);
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = fixp_linear_interpolate(BATTERY_MIN, 0,
+						      BATTERY_MAX, 100,
+						      battery_energy);
+		break;
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int drc_setup_battery(struct drc *drc,
+			     struct hid_device *hdev)
+{
+	struct power_supply_config psy_cfg = { .drv_data = drc, };
+	static atomic_t drc_num = ATOMIC_INIT(0);
+	int ret;
+
+	spin_lock_init(&drc->battery_lock);
+
+	drc->battery_desc.properties = drc_battery_props;
+	drc->battery_desc.num_properties = ARRAY_SIZE(drc_battery_props);
+	drc->battery_desc.get_property = drc_battery_get_property;
+	drc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+	drc->battery_desc.use_for_apm = 0;
+
+	drc->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
+						"wiiu-drc-%i-battery", atomic_fetch_inc(&drc_num));
+	if (!drc->battery_desc.name)
+		return -ENOMEM;
+
+	drc->battery = devm_power_supply_register(&hdev->dev, &drc->battery_desc, &psy_cfg);
+	if (IS_ERR(drc->battery)) {
+		ret = PTR_ERR(drc->battery);
+		hid_err(hdev, "Unable to register battery device\n");
+		return ret;
+	}
+
+	power_supply_powers(drc->battery, &hdev->dev);
+
+	return 0;
+}
+#endif
+
 static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
 	struct drc *drc;
@@ -392,6 +520,14 @@ static int drc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		return -ENOMEM;
 	}
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	ret = drc_setup_battery(drc, hdev);
+	if (ret) {
+		hid_err(hdev, "could not allocate battery interface\n");
+		return ret;
+	}
+#endif
+
 	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER);
 	if (ret) {
 		hid_err(hdev, "hw start failed\n");
-- 
2.31.1


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

* Re: [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad
  2021-05-19  8:59 ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
                     ` (3 preceding siblings ...)
  2021-05-19  8:59   ` [PATCH v3 4/4] HID: wiiu-drc: Add battery reporting Emmanuel Gil Peyrot
@ 2021-09-21 15:08   ` Emmanuel Gil Peyrot
  2021-10-19  9:14     ` Jiri Kosina
  2021-10-19 11:04   ` [PATCH v4 0/5] HID: nintendo: Add support " Emmanuel Gil Peyrot
  5 siblings, 1 reply; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-09-21 15:08 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot
  Cc: linux-input, Ash Logan, Jonathan Neuschäfer,
	Barnabás Pőcze, Jiri Kosina, Benjamin Tissoires,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 440 bytes --]

On Wed, May 19, 2021 at 10:59:20AM +0200, Emmanuel Gil Peyrot wrote:
> This driver is for the DRC (wireless gamepad) when plugged to the DRH of
> the Wii U, a chip exposing it as a USB device.

Hi, I’d like to request a review on this series.  I’ve been using it
with success for quite some months, and from a self-review after not
having touched it for as long it still looks correct. :)

Thanks!

-- 
Emmanuel Gil Peyrot

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad
  2021-09-21 15:08   ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
@ 2021-10-19  9:14     ` Jiri Kosina
  2021-10-19  9:27       ` Emmanuel Gil Peyrot
  0 siblings, 1 reply; 41+ messages in thread
From: Jiri Kosina @ 2021-10-19  9:14 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot
  Cc: linux-input, Ash Logan, Jonathan Neuschäfer,
	Barnabás Pőcze, Benjamin Tissoires, linux-kernel,
	Daniel J. Ogorchock

On Tue, 21 Sep 2021, Emmanuel Gil Peyrot wrote:

> > This driver is for the DRC (wireless gamepad) when plugged to the DRH of
> > the Wii U, a chip exposing it as a USB device.
> 
> Hi, I’d like to request a review on this series.  I’ve been using it
> with success for quite some months, and from a self-review after not
> having touched it for as long it still looks correct. :)
> 
> Thanks!

[ CCing Daniel ]

Thanks for the patch, and sorry for the delay in the review.

The code looks good to me, the only question/request I'd have is -- would 
it be possible to adhere to the driver naming standards, and actually 
incorporate the support to existing hid-nintendo driver? Or is there any 
substantial reason which I don't see why this wouldn't be a good idea?

Thanks,

-- 
Jiri Kosina
SUSE Labs


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

* Re: [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad
  2021-10-19  9:14     ` Jiri Kosina
@ 2021-10-19  9:27       ` Emmanuel Gil Peyrot
  2021-10-19  9:30         ` Jiri Kosina
  2021-10-19 23:59         ` François-Xavier Carton
  0 siblings, 2 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-19  9:27 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J. Ogorchock

[-- Attachment #1: Type: text/plain, Size: 1569 bytes --]

On Tue, Oct 19, 2021 at 11:14:02AM +0200, Jiri Kosina wrote:
> On Tue, 21 Sep 2021, Emmanuel Gil Peyrot wrote:
> 
> > > This driver is for the DRC (wireless gamepad) when plugged to the DRH of
> > > the Wii U, a chip exposing it as a USB device.
> > 
> > Hi, I’d like to request a review on this series.  I’ve been using it
> > with success for quite some months, and from a self-review after not
> > having touched it for as long it still looks correct. :)
> > 
> > Thanks!
> 
> [ CCing Daniel ]
> 
> Thanks for the patch, and sorry for the delay in the review.

No worries. :)

> 
> The code looks good to me, the only question/request I'd have is -- would 
> it be possible to adhere to the driver naming standards, and actually 
> incorporate the support to existing hid-nintendo driver? Or is there any 
> substantial reason which I don't see why this wouldn't be a good idea?

I don’t see any existing driver named that way in mainline, would it be
acceptable to simply rename the current patches to hid-nintendo?  What
should be done about the existing hid-wiimote driver then, should it
also be merged alongside?

Another driver I’d like to submit eventually is the GameCube Controller
Adapter for Wii U, which does exactly what its name says, but being an
external USB adapter it also works on any USB computer; would it make
sense to develop it alongside the current driver, just because it is
sold by the same company?

> 
> Thanks,
> 
> -- 
> Jiri Kosina
> SUSE Labs

Thanks,

-- 
Emmanuel Gil Peyrot

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad
  2021-10-19  9:27       ` Emmanuel Gil Peyrot
@ 2021-10-19  9:30         ` Jiri Kosina
  2021-10-19  9:36           ` Emmanuel Gil Peyrot
  2021-11-04 11:21           ` Emmanuel Gil Peyrot
  2021-10-19 23:59         ` François-Xavier Carton
  1 sibling, 2 replies; 41+ messages in thread
From: Jiri Kosina @ 2021-10-19  9:30 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot
  Cc: linux-input, Ash Logan, Jonathan Neuschäfer,
	Barnabás Pőcze, Benjamin Tissoires, linux-kernel,
	Daniel J. Ogorchock

On Tue, 19 Oct 2021, Emmanuel Gil Peyrot wrote:

> > The code looks good to me, the only question/request I'd have is -- would 
> > it be possible to adhere to the driver naming standards, and actually 
> > incorporate the support to existing hid-nintendo driver? Or is there any 
> > substantial reason which I don't see why this wouldn't be a good idea?
> 
> I don’t see any existing driver named that way in mainline, would it be
> acceptable to simply rename the current patches to hid-nintendo?  What
> should be done about the existing hid-wiimote driver then, should it
> also be merged alongside?

hid-nintendo has just recently been staged for 5.16 in 
hid.git#for-5.16/nintendo git branch. Could you please check that?

> Another driver I’d like to submit eventually is the GameCube Controller 
> Adapter for Wii U, which does exactly what its name says, but being an 
> external USB adapter it also works on any USB computer; would it make 
> sense to develop it alongside the current driver, just because it is 
> sold by the same company?

We generally group the support for HID devices in drivers based on the 
producing company, with a few exceptions where it doesn't make sense.

Thanks,

-- 
Jiri Kosina
SUSE Labs


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

* Re: [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad
  2021-10-19  9:30         ` Jiri Kosina
@ 2021-10-19  9:36           ` Emmanuel Gil Peyrot
  2021-11-04 11:21           ` Emmanuel Gil Peyrot
  1 sibling, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-19  9:36 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J. Ogorchock

[-- Attachment #1: Type: text/plain, Size: 1785 bytes --]

On Tue, Oct 19, 2021 at 11:30:06AM +0200, Jiri Kosina wrote:
> On Tue, 19 Oct 2021, Emmanuel Gil Peyrot wrote:
> 
> > > The code looks good to me, the only question/request I'd have is -- would 
> > > it be possible to adhere to the driver naming standards, and actually 
> > > incorporate the support to existing hid-nintendo driver? Or is there any 
> > > substantial reason which I don't see why this wouldn't be a good idea?
> > 
> > I don’t see any existing driver named that way in mainline, would it be
> > acceptable to simply rename the current patches to hid-nintendo?  What
> > should be done about the existing hid-wiimote driver then, should it
> > also be merged alongside?
> 
> hid-nintendo has just recently been staged for 5.16 in 
> hid.git#for-5.16/nintendo git branch. Could you please check that?

Got it, thanks!

> 
> > Another driver I’d like to submit eventually is the GameCube Controller 
> > Adapter for Wii U, which does exactly what its name says, but being an 
> > external USB adapter it also works on any USB computer; would it make 
> > sense to develop it alongside the current driver, just because it is 
> > sold by the same company?
> 
> We generally group the support for HID devices in drivers based on the 
> producing company, with a few exceptions where it doesn't make sense.

Would it make sense to decouple this driver from the joycon driver?  To
have some kind of sub-driver (possibly with Kconfig entries) for joycon,
wiimote, drc, gc-adapter, maybe more in the future?  I don’t see the drc
code go well into that one C file.  I can handle the split of the joycon
into its own file if that’s a good way forward.

> 
> Thanks,
> 
> -- 
> Jiri Kosina
> SUSE Labs

-- 
Emmanuel Gil Peyrot

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* [PATCH v4 0/5] HID: nintendo: Add support for the Wii U gamepad
  2021-05-19  8:59 ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
                     ` (4 preceding siblings ...)
  2021-09-21 15:08   ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
@ 2021-10-19 11:04   ` Emmanuel Gil Peyrot
  2021-10-19 11:04     ` [PATCH v4 1/5] HID: nintendo: split switch support into its own file Emmanuel Gil Peyrot
                       ` (6 more replies)
  5 siblings, 7 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-19 11:04 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock

This driver is for the DRC (wireless gamepad) when plugged to the DRH of
the Wii U, a chip exposing it as a USB device.

I tried to use this driver on master over usbip on my laptop, but usbip
disconnects the device right after the driver created the
/dev/input/event* files, so instead I have only tested this driver on
the 4.19 branch of the linux-wiiu[1] downstream.

Other than that, pretty much all of the HID parts of the gamepad work,
it’s only missing microphone, camera and NFC input now but those are
mostly standard (read require quirks) and pertain to other subsystems,
so I felt like this can be upstreamed already.

[1] https://gitlab.com/linux-wiiu/linux-wiiu

Changes since v1:
- Rename interfaces to be less redundant.
- Add comments for potentially unclear things.
- Reword some commits to include more needed information.
- Include all needed includes.
- Use helpful helper functions instead of (badly) reimplementing them
  myself.
- Always return the correct type for each function, to avoid some
  bool/int confusion, or returning 0 to mean error.
- Use myself as the module author, even though Ash did most of the
  initial work, I’m the one who will be maintaining this module from now
  on.
- Use input_set_capability() instead of set_bit(…, keybit) to also set
  BIT(EV_KEY) on evbit[0].
- Call hid_hw_start() before input_register_device() but after the setup
  functions, so that hid_hw_open() is never called before it.
- Add missing spin_lock_init() for the battery lock.
- Use a static atomic for the drc_num, and remove the comment about
  using the interface number.
- Interpret battery report as the voltage coming from an ADC, instead of
  the completely wrong ENERGY_NOW it was before.

So basically addressing Jonathan’s and Barnabás’ comments. :)

Changes since v2:
- Guard against the possibility of CONFIG_HID_BATTERY_STRENGTH not
  having been selected.
- Include forgotten linux/fixp-arith.h header.
- Fix a warning in clamp() due to comparing a s16 with a #define.

Changes since v3:
- Rebased on top of hid.git#for-5.16/nintendo.
- Merged into hid-nintendo.
- Make hid-nintendo mostly a stub, with sub-drivers implementing the
  actual controllers.
- Introduced CONFIG_HID_NINTENDO_WIIU to enable support for this device
  now.

Ash Logan (1):
  HID: nintendo: drc: add support for the Wii U gamepad

Emmanuel Gil Peyrot (4):
  HID: nintendo: split switch support into its own file
  HID: nintendo: drc: implement touch reports
  HID: nintendo: drc: add accelerometer, gyroscope and magnetometer
    readings
  HID: nintendo: drc: add battery reporting

 drivers/hid/Kconfig                           |   31 +-
 drivers/hid/Makefile                          |    2 +
 drivers/hid/hid-ids.h                         |    1 +
 .../{hid-nintendo.c => hid-nintendo-switch.c} |   43 +-
 drivers/hid/hid-nintendo-wiiu.c               |  550 ++++
 drivers/hid/hid-nintendo.c                    | 2300 +----------------
 drivers/hid/hid-nintendo.h                    |   22 +
 drivers/hid/hid-quirks.c                      |    3 +
 8 files changed, 645 insertions(+), 2307 deletions(-)
 copy drivers/hid/{hid-nintendo.c => hid-nintendo-switch.c} (98%)
 create mode 100644 drivers/hid/hid-nintendo-wiiu.c
 create mode 100644 drivers/hid/hid-nintendo.h

-- 
2.33.1


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

* [PATCH v4 1/5] HID: nintendo: split switch support into its own file
  2021-10-19 11:04   ` [PATCH v4 0/5] HID: nintendo: Add support " Emmanuel Gil Peyrot
@ 2021-10-19 11:04     ` Emmanuel Gil Peyrot
  2021-10-22  8:32       ` kernel test robot
  2021-10-19 11:04     ` [PATCH v4 2/5] HID: nintendo: drc: add support for the Wii U gamepad Emmanuel Gil Peyrot
                       ` (5 subsequent siblings)
  6 siblings, 1 reply; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-19 11:04 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock

This allows for other controllers to be supported, which do not require
the same set of features.

Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/Kconfig                           |   24 +-
 drivers/hid/Makefile                          |    1 +
 .../{hid-nintendo.c => hid-nintendo-switch.c} |   43 +-
 drivers/hid/hid-nintendo.c                    | 2285 +----------------
 drivers/hid/hid-nintendo.h                    |   16 +
 5 files changed, 63 insertions(+), 2306 deletions(-)
 copy drivers/hid/{hid-nintendo.c => hid-nintendo-switch.c} (98%)
 create mode 100644 drivers/hid/hid-nintendo.h

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 62d4a6d6ba13..79beccefd431 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -731,18 +731,13 @@ config HID_MULTITOUCH
 	  module will be called hid-multitouch.
 
 config HID_NINTENDO
-	tristate "Nintendo Joy-Con and Pro Controller support"
+	tristate "Nintendo controllers support"
 	depends on HID
-	depends on NEW_LEDS
-	depends on LEDS_CLASS
-	select POWER_SUPPLY
 	help
-	Adds support for the Nintendo Switch Joy-Cons and Pro Controller.
-	All controllers support bluetooth, and the Pro Controller also supports
-	its USB mode.
+	  Adds support for various Nintendo controllers.
 
-	To compile this driver as a module, choose M here: the
-	module will be called hid-nintendo.
+	  To compile this driver as a module, choose M here: the
+	  module will be called hid-nintendo.
 
 config NINTENDO_FF
 	bool "Nintendo Switch controller force feedback support"
@@ -754,6 +749,17 @@ config NINTENDO_FF
 	controller. For the pro controller, both rumble motors can be controlled
 	individually.
 
+config HID_NINTENDO_SWITCH
+	tristate "Nintendo Wii U gamepad (DRC) over internal DRH"
+	depends on HID_NINTENDO
+	depends on NEW_LEDS
+	depends on LEDS_CLASS
+	select POWER_SUPPLY
+	help
+	  Adds support for the Nintendo Switch Joy-Cons and Pro Controller.
+	  All controllers support bluetooth, and the Pro Controller also supports
+	  its USB mode.
+
 config HID_NTI
 	tristate "NTI keyboard adapters"
 	help
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 7a71371e3adf..a0a9ee182ef2 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_HID_MICROSOFT)	+= hid-microsoft.o
 obj-$(CONFIG_HID_MONTEREY)	+= hid-monterey.o
 obj-$(CONFIG_HID_MULTITOUCH)	+= hid-multitouch.o
 obj-$(CONFIG_HID_NINTENDO)	+= hid-nintendo.o
+obj-$(CONFIG_HID_NINTENDO_SWITCH)	+= hid-nintendo-switch.o
 obj-$(CONFIG_HID_NTI)			+= hid-nti.o
 obj-$(CONFIG_HID_NTRIG)		+= hid-ntrig.o
 obj-$(CONFIG_HID_ORTEK)		+= hid-ortek.o
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo-switch.c
similarity index 98%
copy from drivers/hid/hid-nintendo.c
copy to drivers/hid/hid-nintendo-switch.c
index c4270499fc6f..b742ed45b601 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo-switch.c
@@ -23,6 +23,7 @@
  */
 
 #include "hid-ids.h"
+#include "hid-nintendo.h"
 #include <asm/unaligned.h>
 #include <linux/delay.h>
 #include <linux/device.h>
@@ -31,7 +32,6 @@
 #include <linux/input.h>
 #include <linux/jiffies.h>
 #include <linux/leds.h>
-#include <linux/module.h>
 #include <linux/power_supply.h>
 #include <linux/spinlock.h>
 
@@ -412,6 +412,7 @@ static const char * const joycon_player_led_names[] = {
 
 /* Each physical controller is associated with a joycon_ctlr struct */
 struct joycon_ctlr {
+	enum nintendo_driver driver;
 	struct hid_device *hdev;
 	struct input_dev *input;
 	struct led_classdev leds[JC_NUM_LEDS]; /* player leds */
@@ -2088,8 +2089,8 @@ static int joycon_ctlr_handle_event(struct joycon_ctlr *ctlr, u8 *data,
 	return ret;
 }
 
-static int nintendo_hid_event(struct hid_device *hdev,
-			      struct hid_report *report, u8 *raw_data, int size)
+int switch_hid_event(struct hid_device *hdev,
+		     struct hid_report *report, u8 *raw_data, int size)
 {
 	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
 
@@ -2099,8 +2100,8 @@ static int nintendo_hid_event(struct hid_device *hdev,
 	return joycon_ctlr_handle_event(ctlr, raw_data, size);
 }
 
-static int nintendo_hid_probe(struct hid_device *hdev,
-			    const struct hid_device_id *id)
+int switch_hid_probe(struct hid_device *hdev,
+		     const struct hid_device_id *id)
 {
 	int ret;
 	struct joycon_ctlr *ctlr;
@@ -2113,6 +2114,7 @@ static int nintendo_hid_probe(struct hid_device *hdev,
 		goto err;
 	}
 
+	ctlr->driver = NINTENDO_SWITCH;
 	ctlr->hdev = hdev;
 	ctlr->ctlr_state = JOYCON_CTLR_STATE_INIT;
 	ctlr->rumble_queue_head = JC_RUMBLE_QUEUE_SIZE - 1;
@@ -2271,7 +2273,7 @@ static int nintendo_hid_probe(struct hid_device *hdev,
 	return ret;
 }
 
-static void nintendo_hid_remove(struct hid_device *hdev)
+void switch_hid_remove(struct hid_device *hdev)
 {
 	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
 	unsigned long flags;
@@ -2288,32 +2290,3 @@ static void nintendo_hid_remove(struct hid_device *hdev)
 	hid_hw_close(hdev);
 	hid_hw_stop(hdev);
 }
-
-static const struct hid_device_id nintendo_hid_devices[] = {
-	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
-			 USB_DEVICE_ID_NINTENDO_PROCON) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
-			 USB_DEVICE_ID_NINTENDO_PROCON) },
-	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
-			 USB_DEVICE_ID_NINTENDO_CHRGGRIP) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
-			 USB_DEVICE_ID_NINTENDO_JOYCONL) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
-			 USB_DEVICE_ID_NINTENDO_JOYCONR) },
-	{ }
-};
-MODULE_DEVICE_TABLE(hid, nintendo_hid_devices);
-
-static struct hid_driver nintendo_hid_driver = {
-	.name		= "nintendo",
-	.id_table	= nintendo_hid_devices,
-	.probe		= nintendo_hid_probe,
-	.remove		= nintendo_hid_remove,
-	.raw_event	= nintendo_hid_event,
-};
-module_hid_driver(nintendo_hid_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Daniel J. Ogorchock <djogorchock@gmail.com>");
-MODULE_DESCRIPTION("Driver for Nintendo Switch Controllers");
-
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index c4270499fc6f..9d67c7b2ac75 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -1,2295 +1,54 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * HID driver for Nintendo Switch Joy-Cons and Pro Controllers
- *
- * Copyright (c) 2019-2021 Daniel J. Ogorchock <djogorchock@gmail.com>
- *
- * The following resources/projects were referenced for this driver:
- *   https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
- *   https://gitlab.com/pjranki/joycon-linux-kernel (Peter Rankin)
- *   https://github.com/FrotBot/SwitchProConLinuxUSB
- *   https://github.com/MTCKC/ProconXInput
- *   https://github.com/Davidobot/BetterJoyForCemu
- *   hid-wiimote kernel hid driver
- *   hid-logitech-hidpp driver
- *   hid-sony driver
- *
- * This driver supports the Nintendo Switch Joy-Cons and Pro Controllers. The
- * Pro Controllers can either be used over USB or Bluetooth.
- *
- * The driver will retrieve the factory calibration info from the controllers,
- * so little to no user calibration should be required.
+ * HID driver for Nintendo controllers
  *
+ * Copyright (c) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  */
 
 #include "hid-ids.h"
-#include <asm/unaligned.h>
-#include <linux/delay.h>
+#include "hid-nintendo.h"
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/hid.h>
 #include <linux/input.h>
-#include <linux/jiffies.h>
-#include <linux/leds.h>
 #include <linux/module.h>
-#include <linux/power_supply.h>
-#include <linux/spinlock.h>
-
-/*
- * Reference the url below for the following HID report defines:
- * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
- */
-
-/* Output Reports */
-static const u8 JC_OUTPUT_RUMBLE_AND_SUBCMD	= 0x01;
-static const u8 JC_OUTPUT_FW_UPDATE_PKT		= 0x03;
-static const u8 JC_OUTPUT_RUMBLE_ONLY		= 0x10;
-static const u8 JC_OUTPUT_MCU_DATA		= 0x11;
-static const u8 JC_OUTPUT_USB_CMD		= 0x80;
-
-/* Subcommand IDs */
-static const u8 JC_SUBCMD_STATE			/*= 0x00*/;
-static const u8 JC_SUBCMD_MANUAL_BT_PAIRING	= 0x01;
-static const u8 JC_SUBCMD_REQ_DEV_INFO		= 0x02;
-static const u8 JC_SUBCMD_SET_REPORT_MODE	= 0x03;
-static const u8 JC_SUBCMD_TRIGGERS_ELAPSED	= 0x04;
-static const u8 JC_SUBCMD_GET_PAGE_LIST_STATE	= 0x05;
-static const u8 JC_SUBCMD_SET_HCI_STATE		= 0x06;
-static const u8 JC_SUBCMD_RESET_PAIRING_INFO	= 0x07;
-static const u8 JC_SUBCMD_LOW_POWER_MODE	= 0x08;
-static const u8 JC_SUBCMD_SPI_FLASH_READ	= 0x10;
-static const u8 JC_SUBCMD_SPI_FLASH_WRITE	= 0x11;
-static const u8 JC_SUBCMD_RESET_MCU		= 0x20;
-static const u8 JC_SUBCMD_SET_MCU_CONFIG	= 0x21;
-static const u8 JC_SUBCMD_SET_MCU_STATE		= 0x22;
-static const u8 JC_SUBCMD_SET_PLAYER_LIGHTS	= 0x30;
-static const u8 JC_SUBCMD_GET_PLAYER_LIGHTS	= 0x31;
-static const u8 JC_SUBCMD_SET_HOME_LIGHT	= 0x38;
-static const u8 JC_SUBCMD_ENABLE_IMU		= 0x40;
-static const u8 JC_SUBCMD_SET_IMU_SENSITIVITY	= 0x41;
-static const u8 JC_SUBCMD_WRITE_IMU_REG		= 0x42;
-static const u8 JC_SUBCMD_READ_IMU_REG		= 0x43;
-static const u8 JC_SUBCMD_ENABLE_VIBRATION	= 0x48;
-static const u8 JC_SUBCMD_GET_REGULATED_VOLTAGE	= 0x50;
-
-/* Input Reports */
-static const u8 JC_INPUT_BUTTON_EVENT		= 0x3F;
-static const u8 JC_INPUT_SUBCMD_REPLY		= 0x21;
-static const u8 JC_INPUT_IMU_DATA		= 0x30;
-static const u8 JC_INPUT_MCU_DATA		= 0x31;
-static const u8 JC_INPUT_USB_RESPONSE		= 0x81;
-
-/* Feature Reports */
-static const u8 JC_FEATURE_LAST_SUBCMD		= 0x02;
-static const u8 JC_FEATURE_OTA_FW_UPGRADE	= 0x70;
-static const u8 JC_FEATURE_SETUP_MEM_READ	= 0x71;
-static const u8 JC_FEATURE_MEM_READ		= 0x72;
-static const u8 JC_FEATURE_ERASE_MEM_SECTOR	= 0x73;
-static const u8 JC_FEATURE_MEM_WRITE		= 0x74;
-static const u8 JC_FEATURE_LAUNCH		= 0x75;
-
-/* USB Commands */
-static const u8 JC_USB_CMD_CONN_STATUS		= 0x01;
-static const u8 JC_USB_CMD_HANDSHAKE		= 0x02;
-static const u8 JC_USB_CMD_BAUDRATE_3M		= 0x03;
-static const u8 JC_USB_CMD_NO_TIMEOUT		= 0x04;
-static const u8 JC_USB_CMD_EN_TIMEOUT		= 0x05;
-static const u8 JC_USB_RESET			= 0x06;
-static const u8 JC_USB_PRE_HANDSHAKE		= 0x91;
-static const u8 JC_USB_SEND_UART		= 0x92;
-
-/* Magic value denoting presence of user calibration */
-static const u16 JC_CAL_USR_MAGIC_0		= 0xB2;
-static const u16 JC_CAL_USR_MAGIC_1		= 0xA1;
-static const u8 JC_CAL_USR_MAGIC_SIZE		= 2;
-
-/* SPI storage addresses of user calibration data */
-static const u16 JC_CAL_USR_LEFT_MAGIC_ADDR	= 0x8010;
-static const u16 JC_CAL_USR_LEFT_DATA_ADDR	= 0x8012;
-static const u16 JC_CAL_USR_LEFT_DATA_END	= 0x801A;
-static const u16 JC_CAL_USR_RIGHT_MAGIC_ADDR	= 0x801B;
-static const u16 JC_CAL_USR_RIGHT_DATA_ADDR	= 0x801D;
-#define JC_CAL_STICK_DATA_SIZE \
-	(JC_CAL_USR_LEFT_DATA_END - JC_CAL_USR_LEFT_DATA_ADDR + 1)
-
-/* SPI storage addresses of factory calibration data */
-static const u16 JC_CAL_FCT_DATA_LEFT_ADDR	= 0x603d;
-static const u16 JC_CAL_FCT_DATA_RIGHT_ADDR	= 0x6046;
-
-/* SPI storage addresses of IMU factory calibration data */
-static const u16 JC_IMU_CAL_FCT_DATA_ADDR	= 0x6020;
-static const u16 JC_IMU_CAL_FCT_DATA_END	= 0x6037;
-#define JC_IMU_CAL_DATA_SIZE \
-	(JC_IMU_CAL_FCT_DATA_END - JC_IMU_CAL_FCT_DATA_ADDR + 1)
-/* SPI storage addresses of IMU user calibration data */
-static const u16 JC_IMU_CAL_USR_MAGIC_ADDR	= 0x8026;
-static const u16 JC_IMU_CAL_USR_DATA_ADDR	= 0x8028;
-
-/* The raw analog joystick values will be mapped in terms of this magnitude */
-static const u16 JC_MAX_STICK_MAG		= 32767;
-static const u16 JC_STICK_FUZZ			= 250;
-static const u16 JC_STICK_FLAT			= 500;
-
-/* Hat values for pro controller's d-pad */
-static const u16 JC_MAX_DPAD_MAG		= 1;
-static const u16 JC_DPAD_FUZZ			/*= 0*/;
-static const u16 JC_DPAD_FLAT			/*= 0*/;
-
-/* Under most circumstances IMU reports are pushed every 15ms; use as default */
-static const u16 JC_IMU_DFLT_AVG_DELTA_MS	= 15;
-/* How many samples to sum before calculating average IMU report delta */
-static const u16 JC_IMU_SAMPLES_PER_DELTA_AVG	= 300;
-/* Controls how many dropped IMU packets at once trigger a warning message */
-static const u16 JC_IMU_DROPPED_PKT_WARNING	= 3;
-
-/*
- * The controller's accelerometer has a sensor resolution of 16bits and is
- * configured with a range of +-8000 milliGs. Therefore, the resolution can be
- * calculated thus: (2^16-1)/(8000 * 2) = 4.096 digits per milliG
- * Resolution per G (rather than per millliG): 4.096 * 1000 = 4096 digits per G
- * Alternatively: 1/4096 = .0002441 Gs per digit
- */
-static const s32 JC_IMU_MAX_ACCEL_MAG		= 32767;
-static const u16 JC_IMU_ACCEL_RES_PER_G		= 4096;
-static const u16 JC_IMU_ACCEL_FUZZ		= 10;
-static const u16 JC_IMU_ACCEL_FLAT		/*= 0*/;
-
-/*
- * The controller's gyroscope has a sensor resolution of 16bits and is
- * configured with a range of +-2000 degrees/second.
- * Digits per dps: (2^16 -1)/(2000*2) = 16.38375
- * dps per digit: 16.38375E-1 = .0610
- *
- * STMicro recommends in the datasheet to add 15% to the dps/digit. This allows
- * the full sensitivity range to be saturated without clipping. This yields more
- * accurate results, so it's the technique this driver uses.
- * dps per digit (corrected): .0610 * 1.15 = .0702
- * digits per dps (corrected): .0702E-1 = 14.247
- *
- * Now, 14.247 truncating to 14 loses a lot of precision, so we rescale the
- * min/max range by 1000.
- */
-static const s32 JC_IMU_PREC_RANGE_SCALE	= 1000;
-/* Note: change mag and res_per_dps if prec_range_scale is ever altered */
-static const s32 JC_IMU_MAX_GYRO_MAG		= 32767000; /* (2^16-1)*1000 */
-static const u16 JC_IMU_GYRO_RES_PER_DPS	= 14247; /* (14.247*1000) */
-static const u16 JC_IMU_GYRO_FUZZ		= 10;
-static const u16 JC_IMU_GYRO_FLAT		/*= 0*/;
-
-/* frequency/amplitude tables for rumble */
-struct joycon_rumble_freq_data {
-	u16 high;
-	u8 low;
-	u16 freq; /* Hz*/
-};
-
-struct joycon_rumble_amp_data {
-	u8 high;
-	u16 low;
-	u16 amp;
-};
-
-/*
- * These tables are from
- * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
- */
-static const struct joycon_rumble_freq_data joycon_rumble_frequencies[] = {
-	/* high, low, freq */
-	{ 0x0000, 0x01,   41 }, { 0x0000, 0x02,   42 }, { 0x0000, 0x03,   43 },
-	{ 0x0000, 0x04,   44 }, { 0x0000, 0x05,   45 }, { 0x0000, 0x06,   46 },
-	{ 0x0000, 0x07,   47 }, { 0x0000, 0x08,   48 }, { 0x0000, 0x09,   49 },
-	{ 0x0000, 0x0A,   50 }, { 0x0000, 0x0B,   51 }, { 0x0000, 0x0C,   52 },
-	{ 0x0000, 0x0D,   53 }, { 0x0000, 0x0E,   54 }, { 0x0000, 0x0F,   55 },
-	{ 0x0000, 0x10,   57 }, { 0x0000, 0x11,   58 }, { 0x0000, 0x12,   59 },
-	{ 0x0000, 0x13,   60 }, { 0x0000, 0x14,   62 }, { 0x0000, 0x15,   63 },
-	{ 0x0000, 0x16,   64 }, { 0x0000, 0x17,   66 }, { 0x0000, 0x18,   67 },
-	{ 0x0000, 0x19,   69 }, { 0x0000, 0x1A,   70 }, { 0x0000, 0x1B,   72 },
-	{ 0x0000, 0x1C,   73 }, { 0x0000, 0x1D,   75 }, { 0x0000, 0x1e,   77 },
-	{ 0x0000, 0x1f,   78 }, { 0x0000, 0x20,   80 }, { 0x0400, 0x21,   82 },
-	{ 0x0800, 0x22,   84 }, { 0x0c00, 0x23,   85 }, { 0x1000, 0x24,   87 },
-	{ 0x1400, 0x25,   89 }, { 0x1800, 0x26,   91 }, { 0x1c00, 0x27,   93 },
-	{ 0x2000, 0x28,   95 }, { 0x2400, 0x29,   97 }, { 0x2800, 0x2a,   99 },
-	{ 0x2c00, 0x2b,  102 }, { 0x3000, 0x2c,  104 }, { 0x3400, 0x2d,  106 },
-	{ 0x3800, 0x2e,  108 }, { 0x3c00, 0x2f,  111 }, { 0x4000, 0x30,  113 },
-	{ 0x4400, 0x31,  116 }, { 0x4800, 0x32,  118 }, { 0x4c00, 0x33,  121 },
-	{ 0x5000, 0x34,  123 }, { 0x5400, 0x35,  126 }, { 0x5800, 0x36,  129 },
-	{ 0x5c00, 0x37,  132 }, { 0x6000, 0x38,  135 }, { 0x6400, 0x39,  137 },
-	{ 0x6800, 0x3a,  141 }, { 0x6c00, 0x3b,  144 }, { 0x7000, 0x3c,  147 },
-	{ 0x7400, 0x3d,  150 }, { 0x7800, 0x3e,  153 }, { 0x7c00, 0x3f,  157 },
-	{ 0x8000, 0x40,  160 }, { 0x8400, 0x41,  164 }, { 0x8800, 0x42,  167 },
-	{ 0x8c00, 0x43,  171 }, { 0x9000, 0x44,  174 }, { 0x9400, 0x45,  178 },
-	{ 0x9800, 0x46,  182 }, { 0x9c00, 0x47,  186 }, { 0xa000, 0x48,  190 },
-	{ 0xa400, 0x49,  194 }, { 0xa800, 0x4a,  199 }, { 0xac00, 0x4b,  203 },
-	{ 0xb000, 0x4c,  207 }, { 0xb400, 0x4d,  212 }, { 0xb800, 0x4e,  217 },
-	{ 0xbc00, 0x4f,  221 }, { 0xc000, 0x50,  226 }, { 0xc400, 0x51,  231 },
-	{ 0xc800, 0x52,  236 }, { 0xcc00, 0x53,  241 }, { 0xd000, 0x54,  247 },
-	{ 0xd400, 0x55,  252 }, { 0xd800, 0x56,  258 }, { 0xdc00, 0x57,  263 },
-	{ 0xe000, 0x58,  269 }, { 0xe400, 0x59,  275 }, { 0xe800, 0x5a,  281 },
-	{ 0xec00, 0x5b,  287 }, { 0xf000, 0x5c,  293 }, { 0xf400, 0x5d,  300 },
-	{ 0xf800, 0x5e,  306 }, { 0xfc00, 0x5f,  313 }, { 0x0001, 0x60,  320 },
-	{ 0x0401, 0x61,  327 }, { 0x0801, 0x62,  334 }, { 0x0c01, 0x63,  341 },
-	{ 0x1001, 0x64,  349 }, { 0x1401, 0x65,  357 }, { 0x1801, 0x66,  364 },
-	{ 0x1c01, 0x67,  372 }, { 0x2001, 0x68,  381 }, { 0x2401, 0x69,  389 },
-	{ 0x2801, 0x6a,  397 }, { 0x2c01, 0x6b,  406 }, { 0x3001, 0x6c,  415 },
-	{ 0x3401, 0x6d,  424 }, { 0x3801, 0x6e,  433 }, { 0x3c01, 0x6f,  443 },
-	{ 0x4001, 0x70,  453 }, { 0x4401, 0x71,  462 }, { 0x4801, 0x72,  473 },
-	{ 0x4c01, 0x73,  483 }, { 0x5001, 0x74,  494 }, { 0x5401, 0x75,  504 },
-	{ 0x5801, 0x76,  515 }, { 0x5c01, 0x77,  527 }, { 0x6001, 0x78,  538 },
-	{ 0x6401, 0x79,  550 }, { 0x6801, 0x7a,  562 }, { 0x6c01, 0x7b,  574 },
-	{ 0x7001, 0x7c,  587 }, { 0x7401, 0x7d,  600 }, { 0x7801, 0x7e,  613 },
-	{ 0x7c01, 0x7f,  626 }, { 0x8001, 0x00,  640 }, { 0x8401, 0x00,  654 },
-	{ 0x8801, 0x00,  668 }, { 0x8c01, 0x00,  683 }, { 0x9001, 0x00,  698 },
-	{ 0x9401, 0x00,  713 }, { 0x9801, 0x00,  729 }, { 0x9c01, 0x00,  745 },
-	{ 0xa001, 0x00,  761 }, { 0xa401, 0x00,  778 }, { 0xa801, 0x00,  795 },
-	{ 0xac01, 0x00,  812 }, { 0xb001, 0x00,  830 }, { 0xb401, 0x00,  848 },
-	{ 0xb801, 0x00,  867 }, { 0xbc01, 0x00,  886 }, { 0xc001, 0x00,  905 },
-	{ 0xc401, 0x00,  925 }, { 0xc801, 0x00,  945 }, { 0xcc01, 0x00,  966 },
-	{ 0xd001, 0x00,  987 }, { 0xd401, 0x00, 1009 }, { 0xd801, 0x00, 1031 },
-	{ 0xdc01, 0x00, 1053 }, { 0xe001, 0x00, 1076 }, { 0xe401, 0x00, 1100 },
-	{ 0xe801, 0x00, 1124 }, { 0xec01, 0x00, 1149 }, { 0xf001, 0x00, 1174 },
-	{ 0xf401, 0x00, 1199 }, { 0xf801, 0x00, 1226 }, { 0xfc01, 0x00, 1253 }
-};
-
-#define joycon_max_rumble_amp	(1003)
-static const struct joycon_rumble_amp_data joycon_rumble_amplitudes[] = {
-	/* high, low, amp */
-	{ 0x00, 0x0040,    0 },
-	{ 0x02, 0x8040,   10 }, { 0x04, 0x0041,   12 }, { 0x06, 0x8041,   14 },
-	{ 0x08, 0x0042,   17 }, { 0x0a, 0x8042,   20 }, { 0x0c, 0x0043,   24 },
-	{ 0x0e, 0x8043,   28 }, { 0x10, 0x0044,   33 }, { 0x12, 0x8044,   40 },
-	{ 0x14, 0x0045,   47 }, { 0x16, 0x8045,   56 }, { 0x18, 0x0046,   67 },
-	{ 0x1a, 0x8046,   80 }, { 0x1c, 0x0047,   95 }, { 0x1e, 0x8047,  112 },
-	{ 0x20, 0x0048,  117 }, { 0x22, 0x8048,  123 }, { 0x24, 0x0049,  128 },
-	{ 0x26, 0x8049,  134 }, { 0x28, 0x004a,  140 }, { 0x2a, 0x804a,  146 },
-	{ 0x2c, 0x004b,  152 }, { 0x2e, 0x804b,  159 }, { 0x30, 0x004c,  166 },
-	{ 0x32, 0x804c,  173 }, { 0x34, 0x004d,  181 }, { 0x36, 0x804d,  189 },
-	{ 0x38, 0x004e,  198 }, { 0x3a, 0x804e,  206 }, { 0x3c, 0x004f,  215 },
-	{ 0x3e, 0x804f,  225 }, { 0x40, 0x0050,  230 }, { 0x42, 0x8050,  235 },
-	{ 0x44, 0x0051,  240 }, { 0x46, 0x8051,  245 }, { 0x48, 0x0052,  251 },
-	{ 0x4a, 0x8052,  256 }, { 0x4c, 0x0053,  262 }, { 0x4e, 0x8053,  268 },
-	{ 0x50, 0x0054,  273 }, { 0x52, 0x8054,  279 }, { 0x54, 0x0055,  286 },
-	{ 0x56, 0x8055,  292 }, { 0x58, 0x0056,  298 }, { 0x5a, 0x8056,  305 },
-	{ 0x5c, 0x0057,  311 }, { 0x5e, 0x8057,  318 }, { 0x60, 0x0058,  325 },
-	{ 0x62, 0x8058,  332 }, { 0x64, 0x0059,  340 }, { 0x66, 0x8059,  347 },
-	{ 0x68, 0x005a,  355 }, { 0x6a, 0x805a,  362 }, { 0x6c, 0x005b,  370 },
-	{ 0x6e, 0x805b,  378 }, { 0x70, 0x005c,  387 }, { 0x72, 0x805c,  395 },
-	{ 0x74, 0x005d,  404 }, { 0x76, 0x805d,  413 }, { 0x78, 0x005e,  422 },
-	{ 0x7a, 0x805e,  431 }, { 0x7c, 0x005f,  440 }, { 0x7e, 0x805f,  450 },
-	{ 0x80, 0x0060,  460 }, { 0x82, 0x8060,  470 }, { 0x84, 0x0061,  480 },
-	{ 0x86, 0x8061,  491 }, { 0x88, 0x0062,  501 }, { 0x8a, 0x8062,  512 },
-	{ 0x8c, 0x0063,  524 }, { 0x8e, 0x8063,  535 }, { 0x90, 0x0064,  547 },
-	{ 0x92, 0x8064,  559 }, { 0x94, 0x0065,  571 }, { 0x96, 0x8065,  584 },
-	{ 0x98, 0x0066,  596 }, { 0x9a, 0x8066,  609 }, { 0x9c, 0x0067,  623 },
-	{ 0x9e, 0x8067,  636 }, { 0xa0, 0x0068,  650 }, { 0xa2, 0x8068,  665 },
-	{ 0xa4, 0x0069,  679 }, { 0xa6, 0x8069,  694 }, { 0xa8, 0x006a,  709 },
-	{ 0xaa, 0x806a,  725 }, { 0xac, 0x006b,  741 }, { 0xae, 0x806b,  757 },
-	{ 0xb0, 0x006c,  773 }, { 0xb2, 0x806c,  790 }, { 0xb4, 0x006d,  808 },
-	{ 0xb6, 0x806d,  825 }, { 0xb8, 0x006e,  843 }, { 0xba, 0x806e,  862 },
-	{ 0xbc, 0x006f,  881 }, { 0xbe, 0x806f,  900 }, { 0xc0, 0x0070,  920 },
-	{ 0xc2, 0x8070,  940 }, { 0xc4, 0x0071,  960 }, { 0xc6, 0x8071,  981 },
-	{ 0xc8, 0x0072, joycon_max_rumble_amp }
-};
-
-/* States for controller state machine */
-enum joycon_ctlr_state {
-	JOYCON_CTLR_STATE_INIT,
-	JOYCON_CTLR_STATE_READ,
-	JOYCON_CTLR_STATE_REMOVED,
-};
-
-/* Controller type received as part of device info */
-enum joycon_ctlr_type {
-	JOYCON_CTLR_TYPE_JCL = 0x01,
-	JOYCON_CTLR_TYPE_JCR = 0x02,
-	JOYCON_CTLR_TYPE_PRO = 0x03,
-};
-
-struct joycon_stick_cal {
-	s32 max;
-	s32 min;
-	s32 center;
-};
-
-struct joycon_imu_cal {
-	s16 offset[3];
-	s16 scale[3];
-};
-
-/*
- * All the controller's button values are stored in a u32.
- * They can be accessed with bitwise ANDs.
- */
-static const u32 JC_BTN_Y	= BIT(0);
-static const u32 JC_BTN_X	= BIT(1);
-static const u32 JC_BTN_B	= BIT(2);
-static const u32 JC_BTN_A	= BIT(3);
-static const u32 JC_BTN_SR_R	= BIT(4);
-static const u32 JC_BTN_SL_R	= BIT(5);
-static const u32 JC_BTN_R	= BIT(6);
-static const u32 JC_BTN_ZR	= BIT(7);
-static const u32 JC_BTN_MINUS	= BIT(8);
-static const u32 JC_BTN_PLUS	= BIT(9);
-static const u32 JC_BTN_RSTICK	= BIT(10);
-static const u32 JC_BTN_LSTICK	= BIT(11);
-static const u32 JC_BTN_HOME	= BIT(12);
-static const u32 JC_BTN_CAP	= BIT(13); /* capture button */
-static const u32 JC_BTN_DOWN	= BIT(16);
-static const u32 JC_BTN_UP	= BIT(17);
-static const u32 JC_BTN_RIGHT	= BIT(18);
-static const u32 JC_BTN_LEFT	= BIT(19);
-static const u32 JC_BTN_SR_L	= BIT(20);
-static const u32 JC_BTN_SL_L	= BIT(21);
-static const u32 JC_BTN_L	= BIT(22);
-static const u32 JC_BTN_ZL	= BIT(23);
-
-enum joycon_msg_type {
-	JOYCON_MSG_TYPE_NONE,
-	JOYCON_MSG_TYPE_USB,
-	JOYCON_MSG_TYPE_SUBCMD,
-};
-
-struct joycon_rumble_output {
-	u8 output_id;
-	u8 packet_num;
-	u8 rumble_data[8];
-} __packed;
-
-struct joycon_subcmd_request {
-	u8 output_id; /* must be 0x01 for subcommand, 0x10 for rumble only */
-	u8 packet_num; /* incremented every send */
-	u8 rumble_data[8];
-	u8 subcmd_id;
-	u8 data[]; /* length depends on the subcommand */
-} __packed;
-
-struct joycon_subcmd_reply {
-	u8 ack; /* MSB 1 for ACK, 0 for NACK */
-	u8 id; /* id of requested subcmd */
-	u8 data[]; /* will be at most 35 bytes */
-} __packed;
-
-struct joycon_imu_data {
-	s16 accel_x;
-	s16 accel_y;
-	s16 accel_z;
-	s16 gyro_x;
-	s16 gyro_y;
-	s16 gyro_z;
-} __packed;
-
-struct joycon_input_report {
-	u8 id;
-	u8 timer;
-	u8 bat_con; /* battery and connection info */
-	u8 button_status[3];
-	u8 left_stick[3];
-	u8 right_stick[3];
-	u8 vibrator_report;
-
-	union {
-		struct joycon_subcmd_reply subcmd_reply;
-		/* IMU input reports contain 3 samples */
-		u8 imu_raw_bytes[sizeof(struct joycon_imu_data) * 3];
-	};
-} __packed;
-
-#define JC_MAX_RESP_SIZE	(sizeof(struct joycon_input_report) + 35)
-#define JC_RUMBLE_DATA_SIZE	8
-#define JC_RUMBLE_QUEUE_SIZE	8
-
-static const u16 JC_RUMBLE_DFLT_LOW_FREQ = 160;
-static const u16 JC_RUMBLE_DFLT_HIGH_FREQ = 320;
-static const u16 JC_RUMBLE_PERIOD_MS = 50;
-static const unsigned short JC_RUMBLE_ZERO_AMP_PKT_CNT = 5;
-
-static const char * const joycon_player_led_names[] = {
-	LED_FUNCTION_PLAYER1,
-	LED_FUNCTION_PLAYER2,
-	LED_FUNCTION_PLAYER3,
-	LED_FUNCTION_PLAYER4,
-};
-#define JC_NUM_LEDS		ARRAY_SIZE(joycon_player_led_names)
-
-/* Each physical controller is associated with a joycon_ctlr struct */
-struct joycon_ctlr {
-	struct hid_device *hdev;
-	struct input_dev *input;
-	struct led_classdev leds[JC_NUM_LEDS]; /* player leds */
-	struct led_classdev home_led;
-	enum joycon_ctlr_state ctlr_state;
-	spinlock_t lock;
-	u8 mac_addr[6];
-	char *mac_addr_str;
-	enum joycon_ctlr_type ctlr_type;
-
-	/* The following members are used for synchronous sends/receives */
-	enum joycon_msg_type msg_type;
-	u8 subcmd_num;
-	struct mutex output_mutex;
-	u8 input_buf[JC_MAX_RESP_SIZE];
-	wait_queue_head_t wait;
-	bool received_resp;
-	u8 usb_ack_match;
-	u8 subcmd_ack_match;
-	bool received_input_report;
-	unsigned int last_subcmd_sent_msecs;
-
-	/* factory calibration data */
-	struct joycon_stick_cal left_stick_cal_x;
-	struct joycon_stick_cal left_stick_cal_y;
-	struct joycon_stick_cal right_stick_cal_x;
-	struct joycon_stick_cal right_stick_cal_y;
-
-	struct joycon_imu_cal accel_cal;
-	struct joycon_imu_cal gyro_cal;
-
-	/* prevents needlessly recalculating these divisors every sample */
-	s32 imu_cal_accel_divisor[3];
-	s32 imu_cal_gyro_divisor[3];
-
-	/* power supply data */
-	struct power_supply *battery;
-	struct power_supply_desc battery_desc;
-	u8 battery_capacity;
-	bool battery_charging;
-	bool host_powered;
-
-	/* rumble */
-	u8 rumble_data[JC_RUMBLE_QUEUE_SIZE][JC_RUMBLE_DATA_SIZE];
-	int rumble_queue_head;
-	int rumble_queue_tail;
-	struct workqueue_struct *rumble_queue;
-	struct work_struct rumble_worker;
-	unsigned int rumble_msecs;
-	u16 rumble_ll_freq;
-	u16 rumble_lh_freq;
-	u16 rumble_rl_freq;
-	u16 rumble_rh_freq;
-	unsigned short rumble_zero_countdown;
-
-	/* imu */
-	struct input_dev *imu_input;
-	bool imu_first_packet_received; /* helps in initiating timestamp */
-	unsigned int imu_timestamp_us; /* timestamp we report to userspace */
-	unsigned int imu_last_pkt_ms; /* used to calc imu report delta */
-	/* the following are used to track the average imu report time delta */
-	unsigned int imu_delta_samples_count;
-	unsigned int imu_delta_samples_sum;
-	unsigned int imu_avg_delta_ms;
-};
-
-/* Helper macros for checking controller type */
-#define jc_type_is_joycon(ctlr) \
-	(ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONL || \
-	 ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONR || \
-	 ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP)
-#define jc_type_is_procon(ctlr) \
-	(ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_PROCON)
-#define jc_type_is_chrggrip(ctlr) \
-	(ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP)
-
-/* Does this controller have inputs associated with left joycon? */
-#define jc_type_has_left(ctlr) \
-	(ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCL || \
-	 ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO)
-
-/* Does this controller have inputs associated with right joycon? */
-#define jc_type_has_right(ctlr) \
-	(ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCR || \
-	 ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO)
-
-static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len)
-{
-	u8 *buf;
-	int ret;
-
-	buf = kmemdup(data, len, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-	ret = hid_hw_output_report(hdev, buf, len);
-	kfree(buf);
-	if (ret < 0)
-		hid_dbg(hdev, "Failed to send output report ret=%d\n", ret);
-	return ret;
-}
-
-static void joycon_wait_for_input_report(struct joycon_ctlr *ctlr)
-{
-	int ret;
-
-	/*
-	 * If we are in the proper reporting mode, wait for an input
-	 * report prior to sending the subcommand. This improves
-	 * reliability considerably.
-	 */
-	if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) {
-		unsigned long flags;
-
-		spin_lock_irqsave(&ctlr->lock, flags);
-		ctlr->received_input_report = false;
-		spin_unlock_irqrestore(&ctlr->lock, flags);
-		ret = wait_event_timeout(ctlr->wait,
-					 ctlr->received_input_report,
-					 HZ / 4);
-		/* We will still proceed, even with a timeout here */
-		if (!ret)
-			hid_warn(ctlr->hdev,
-				 "timeout waiting for input report\n");
-	}
-}
-
-/*
- * Sending subcommands and/or rumble data at too high a rate can cause bluetooth
- * controller disconnections.
- */
-static void joycon_enforce_subcmd_rate(struct joycon_ctlr *ctlr)
-{
-	static const unsigned int max_subcmd_rate_ms = 25;
-	unsigned int current_ms = jiffies_to_msecs(jiffies);
-	unsigned int delta_ms = current_ms - ctlr->last_subcmd_sent_msecs;
-
-	while (delta_ms < max_subcmd_rate_ms &&
-	       ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) {
-		joycon_wait_for_input_report(ctlr);
-		current_ms = jiffies_to_msecs(jiffies);
-		delta_ms = current_ms - ctlr->last_subcmd_sent_msecs;
-	}
-	ctlr->last_subcmd_sent_msecs = current_ms;
-}
-
-static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len,
-				u32 timeout)
-{
-	int ret;
-	int tries = 2;
-
-	/*
-	 * The controller occasionally seems to drop subcommands. In testing,
-	 * doing one retry after a timeout appears to always work.
-	 */
-	while (tries--) {
-		joycon_enforce_subcmd_rate(ctlr);
-
-		ret = __joycon_hid_send(ctlr->hdev, data, len);
-		if (ret < 0) {
-			memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE);
-			return ret;
-		}
-
-		ret = wait_event_timeout(ctlr->wait, ctlr->received_resp,
-					 timeout);
-		if (!ret) {
-			hid_dbg(ctlr->hdev,
-				"synchronous send/receive timed out\n");
-			if (tries) {
-				hid_dbg(ctlr->hdev,
-					"retrying sync send after timeout\n");
-			}
-			memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE);
-			ret = -ETIMEDOUT;
-		} else {
-			ret = 0;
-			break;
-		}
-	}
-
-	ctlr->received_resp = false;
-	return ret;
-}
-
-static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd, u32 timeout)
-{
-	int ret;
-	u8 buf[2] = {JC_OUTPUT_USB_CMD};
-
-	buf[1] = cmd;
-	ctlr->usb_ack_match = cmd;
-	ctlr->msg_type = JOYCON_MSG_TYPE_USB;
-	ret = joycon_hid_send_sync(ctlr, buf, sizeof(buf), timeout);
-	if (ret)
-		hid_dbg(ctlr->hdev, "send usb command failed; ret=%d\n", ret);
-	return ret;
-}
-
-static int joycon_send_subcmd(struct joycon_ctlr *ctlr,
-			      struct joycon_subcmd_request *subcmd,
-			      size_t data_len, u32 timeout)
-{
-	int ret;
-	unsigned long flags;
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	/*
-	 * If the controller has been removed, just return ENODEV so the LED
-	 * subsystem doesn't print invalid errors on removal.
-	 */
-	if (ctlr->ctlr_state == JOYCON_CTLR_STATE_REMOVED) {
-		spin_unlock_irqrestore(&ctlr->lock, flags);
-		return -ENODEV;
-	}
-	memcpy(subcmd->rumble_data, ctlr->rumble_data[ctlr->rumble_queue_tail],
-	       JC_RUMBLE_DATA_SIZE);
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	subcmd->output_id = JC_OUTPUT_RUMBLE_AND_SUBCMD;
-	subcmd->packet_num = ctlr->subcmd_num;
-	if (++ctlr->subcmd_num > 0xF)
-		ctlr->subcmd_num = 0;
-	ctlr->subcmd_ack_match = subcmd->subcmd_id;
-	ctlr->msg_type = JOYCON_MSG_TYPE_SUBCMD;
-
-	ret = joycon_hid_send_sync(ctlr, (u8 *)subcmd,
-				   sizeof(*subcmd) + data_len, timeout);
-	if (ret < 0)
-		hid_dbg(ctlr->hdev, "send subcommand failed; ret=%d\n", ret);
-	else
-		ret = 0;
-	return ret;
-}
-
-/* Supply nibbles for flash and on. Ones correspond to active */
-static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on)
-{
-	struct joycon_subcmd_request *req;
-	u8 buffer[sizeof(*req) + 1] = { 0 };
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_SET_PLAYER_LIGHTS;
-	req->data[0] = (flash << 4) | on;
-
-	hid_dbg(ctlr->hdev, "setting player leds\n");
-	return joycon_send_subcmd(ctlr, req, 1, HZ/4);
-}
-
-static int joycon_request_spi_flash_read(struct joycon_ctlr *ctlr,
-					 u32 start_addr, u8 size, u8 **reply)
-{
-	struct joycon_subcmd_request *req;
-	struct joycon_input_report *report;
-	u8 buffer[sizeof(*req) + 5] = { 0 };
-	u8 *data;
-	int ret;
-
-	if (!reply)
-		return -EINVAL;
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_SPI_FLASH_READ;
-	data = req->data;
-	put_unaligned_le32(start_addr, data);
-	data[4] = size;
-
-	hid_dbg(ctlr->hdev, "requesting SPI flash data\n");
-	ret = joycon_send_subcmd(ctlr, req, 5, HZ);
-	if (ret) {
-		hid_err(ctlr->hdev, "failed reading SPI flash; ret=%d\n", ret);
-	} else {
-		report = (struct joycon_input_report *)ctlr->input_buf;
-		/* The read data starts at the 6th byte */
-		*reply = &report->subcmd_reply.data[5];
-	}
-	return ret;
-}
-
-/*
- * User calibration's presence is denoted with a magic byte preceding it.
- * returns 0 if magic val is present, 1 if not present, < 0 on error
- */
-static int joycon_check_for_cal_magic(struct joycon_ctlr *ctlr, u32 flash_addr)
-{
-	int ret;
-	u8 *reply;
-
-	ret = joycon_request_spi_flash_read(ctlr, flash_addr,
-					    JC_CAL_USR_MAGIC_SIZE, &reply);
-	if (ret)
-		return ret;
-
-	return reply[0] != JC_CAL_USR_MAGIC_0 || reply[1] != JC_CAL_USR_MAGIC_1;
-}
-
-static int joycon_read_stick_calibration(struct joycon_ctlr *ctlr, u16 cal_addr,
-					 struct joycon_stick_cal *cal_x,
-					 struct joycon_stick_cal *cal_y,
-					 bool left_stick)
-{
-	s32 x_max_above;
-	s32 x_min_below;
-	s32 y_max_above;
-	s32 y_min_below;
-	u8 *raw_cal;
-	int ret;
-
-	ret = joycon_request_spi_flash_read(ctlr, cal_addr,
-					    JC_CAL_STICK_DATA_SIZE, &raw_cal);
-	if (ret)
-		return ret;
-
-	/* stick calibration parsing: note the order differs based on stick */
-	if (left_stick) {
-		x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0,
-						12);
-		y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4,
-						12);
-		cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0,
-						  12);
-		cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4,
-						  12);
-		x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0,
-						12);
-		y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4,
-						12);
-	} else {
-		cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0,
-						  12);
-		cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4,
-						  12);
-		x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0,
-						12);
-		y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4,
-						12);
-		x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0,
-						12);
-		y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4,
-						12);
-	}
-
-	cal_x->max = cal_x->center + x_max_above;
-	cal_x->min = cal_x->center - x_min_below;
-	cal_y->max = cal_y->center + y_max_above;
-	cal_y->min = cal_y->center - y_min_below;
-
-	return 0;
-}
-
-static const u16 DFLT_STICK_CAL_CEN = 2000;
-static const u16 DFLT_STICK_CAL_MAX = 3500;
-static const u16 DFLT_STICK_CAL_MIN = 500;
-static int joycon_request_calibration(struct joycon_ctlr *ctlr)
-{
-	u16 left_stick_addr = JC_CAL_FCT_DATA_LEFT_ADDR;
-	u16 right_stick_addr = JC_CAL_FCT_DATA_RIGHT_ADDR;
-	int ret;
-
-	hid_dbg(ctlr->hdev, "requesting cal data\n");
-
-	/* check if user stick calibrations are present */
-	if (!joycon_check_for_cal_magic(ctlr, JC_CAL_USR_LEFT_MAGIC_ADDR)) {
-		left_stick_addr = JC_CAL_USR_LEFT_DATA_ADDR;
-		hid_info(ctlr->hdev, "using user cal for left stick\n");
-	} else {
-		hid_info(ctlr->hdev, "using factory cal for left stick\n");
-	}
-	if (!joycon_check_for_cal_magic(ctlr, JC_CAL_USR_RIGHT_MAGIC_ADDR)) {
-		right_stick_addr = JC_CAL_USR_RIGHT_DATA_ADDR;
-		hid_info(ctlr->hdev, "using user cal for right stick\n");
-	} else {
-		hid_info(ctlr->hdev, "using factory cal for right stick\n");
-	}
-
-	/* read the left stick calibration data */
-	ret = joycon_read_stick_calibration(ctlr, left_stick_addr,
-					    &ctlr->left_stick_cal_x,
-					    &ctlr->left_stick_cal_y,
-					    true);
-	if (ret) {
-		hid_warn(ctlr->hdev,
-			 "Failed to read left stick cal, using dflts; e=%d\n",
-			 ret);
-
-		ctlr->left_stick_cal_x.center = DFLT_STICK_CAL_CEN;
-		ctlr->left_stick_cal_x.max = DFLT_STICK_CAL_MAX;
-		ctlr->left_stick_cal_x.min = DFLT_STICK_CAL_MIN;
-
-		ctlr->left_stick_cal_y.center = DFLT_STICK_CAL_CEN;
-		ctlr->left_stick_cal_y.max = DFLT_STICK_CAL_MAX;
-		ctlr->left_stick_cal_y.min = DFLT_STICK_CAL_MIN;
-	}
-
-	/* read the right stick calibration data */
-	ret = joycon_read_stick_calibration(ctlr, right_stick_addr,
-					    &ctlr->right_stick_cal_x,
-					    &ctlr->right_stick_cal_y,
-					    false);
-	if (ret) {
-		hid_warn(ctlr->hdev,
-			 "Failed to read right stick cal, using dflts; e=%d\n",
-			 ret);
-
-		ctlr->right_stick_cal_x.center = DFLT_STICK_CAL_CEN;
-		ctlr->right_stick_cal_x.max = DFLT_STICK_CAL_MAX;
-		ctlr->right_stick_cal_x.min = DFLT_STICK_CAL_MIN;
-
-		ctlr->right_stick_cal_y.center = DFLT_STICK_CAL_CEN;
-		ctlr->right_stick_cal_y.max = DFLT_STICK_CAL_MAX;
-		ctlr->right_stick_cal_y.min = DFLT_STICK_CAL_MIN;
-	}
-
-	hid_dbg(ctlr->hdev, "calibration:\n"
-			    "l_x_c=%d l_x_max=%d l_x_min=%d\n"
-			    "l_y_c=%d l_y_max=%d l_y_min=%d\n"
-			    "r_x_c=%d r_x_max=%d r_x_min=%d\n"
-			    "r_y_c=%d r_y_max=%d r_y_min=%d\n",
-			    ctlr->left_stick_cal_x.center,
-			    ctlr->left_stick_cal_x.max,
-			    ctlr->left_stick_cal_x.min,
-			    ctlr->left_stick_cal_y.center,
-			    ctlr->left_stick_cal_y.max,
-			    ctlr->left_stick_cal_y.min,
-			    ctlr->right_stick_cal_x.center,
-			    ctlr->right_stick_cal_x.max,
-			    ctlr->right_stick_cal_x.min,
-			    ctlr->right_stick_cal_y.center,
-			    ctlr->right_stick_cal_y.max,
-			    ctlr->right_stick_cal_y.min);
-
-	return 0;
-}
-
-/*
- * These divisors are calculated once rather than for each sample. They are only
- * dependent on the IMU calibration values. They are used when processing the
- * IMU input reports.
- */
-static void joycon_calc_imu_cal_divisors(struct joycon_ctlr *ctlr)
-{
-	int i;
-
-	for (i = 0; i < 3; i++) {
-		ctlr->imu_cal_accel_divisor[i] = ctlr->accel_cal.scale[i] -
-						ctlr->accel_cal.offset[i];
-		ctlr->imu_cal_gyro_divisor[i] = ctlr->gyro_cal.scale[i] -
-						ctlr->gyro_cal.offset[i];
-	}
-}
-
-static const s16 DFLT_ACCEL_OFFSET /*= 0*/;
-static const s16 DFLT_ACCEL_SCALE = 16384;
-static const s16 DFLT_GYRO_OFFSET /*= 0*/;
-static const s16 DFLT_GYRO_SCALE  = 13371;
-static int joycon_request_imu_calibration(struct joycon_ctlr *ctlr)
-{
-	u16 imu_cal_addr = JC_IMU_CAL_FCT_DATA_ADDR;
-	u8 *raw_cal;
-	int ret;
-	int i;
-
-	/* check if user calibration exists */
-	if (!joycon_check_for_cal_magic(ctlr, JC_IMU_CAL_USR_MAGIC_ADDR)) {
-		imu_cal_addr = JC_IMU_CAL_USR_DATA_ADDR;
-		hid_info(ctlr->hdev, "using user cal for IMU\n");
-	} else {
-		hid_info(ctlr->hdev, "using factory cal for IMU\n");
-	}
-
-	/* request IMU calibration data */
-	hid_dbg(ctlr->hdev, "requesting IMU cal data\n");
-	ret = joycon_request_spi_flash_read(ctlr, imu_cal_addr,
-					    JC_IMU_CAL_DATA_SIZE, &raw_cal);
-	if (ret) {
-		hid_warn(ctlr->hdev,
-			 "Failed to read IMU cal, using defaults; ret=%d\n",
-			 ret);
-
-		for (i = 0; i < 3; i++) {
-			ctlr->accel_cal.offset[i] = DFLT_ACCEL_OFFSET;
-			ctlr->accel_cal.scale[i] = DFLT_ACCEL_SCALE;
-			ctlr->gyro_cal.offset[i] = DFLT_GYRO_OFFSET;
-			ctlr->gyro_cal.scale[i] = DFLT_GYRO_SCALE;
-		}
-		joycon_calc_imu_cal_divisors(ctlr);
-		return ret;
-	}
-
-	/* IMU calibration parsing */
-	for (i = 0; i < 3; i++) {
-		int j = i * 2;
-
-		ctlr->accel_cal.offset[i] = get_unaligned_le16(raw_cal + j);
-		ctlr->accel_cal.scale[i] = get_unaligned_le16(raw_cal + j + 6);
-		ctlr->gyro_cal.offset[i] = get_unaligned_le16(raw_cal + j + 12);
-		ctlr->gyro_cal.scale[i] = get_unaligned_le16(raw_cal + j + 18);
-	}
-
-	joycon_calc_imu_cal_divisors(ctlr);
-
-	hid_dbg(ctlr->hdev, "IMU calibration:\n"
-			    "a_o[0]=%d a_o[1]=%d a_o[2]=%d\n"
-			    "a_s[0]=%d a_s[1]=%d a_s[2]=%d\n"
-			    "g_o[0]=%d g_o[1]=%d g_o[2]=%d\n"
-			    "g_s[0]=%d g_s[1]=%d g_s[2]=%d\n",
-			    ctlr->accel_cal.offset[0],
-			    ctlr->accel_cal.offset[1],
-			    ctlr->accel_cal.offset[2],
-			    ctlr->accel_cal.scale[0],
-			    ctlr->accel_cal.scale[1],
-			    ctlr->accel_cal.scale[2],
-			    ctlr->gyro_cal.offset[0],
-			    ctlr->gyro_cal.offset[1],
-			    ctlr->gyro_cal.offset[2],
-			    ctlr->gyro_cal.scale[0],
-			    ctlr->gyro_cal.scale[1],
-			    ctlr->gyro_cal.scale[2]);
-
-	return 0;
-}
-
-static int joycon_set_report_mode(struct joycon_ctlr *ctlr)
-{
-	struct joycon_subcmd_request *req;
-	u8 buffer[sizeof(*req) + 1] = { 0 };
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_SET_REPORT_MODE;
-	req->data[0] = 0x30; /* standard, full report mode */
-
-	hid_dbg(ctlr->hdev, "setting controller report mode\n");
-	return joycon_send_subcmd(ctlr, req, 1, HZ);
-}
-
-static int joycon_enable_rumble(struct joycon_ctlr *ctlr)
-{
-	struct joycon_subcmd_request *req;
-	u8 buffer[sizeof(*req) + 1] = { 0 };
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_ENABLE_VIBRATION;
-	req->data[0] = 0x01; /* note: 0x00 would disable */
-
-	hid_dbg(ctlr->hdev, "enabling rumble\n");
-	return joycon_send_subcmd(ctlr, req, 1, HZ/4);
-}
-
-static int joycon_enable_imu(struct joycon_ctlr *ctlr)
-{
-	struct joycon_subcmd_request *req;
-	u8 buffer[sizeof(*req) + 1] = { 0 };
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_ENABLE_IMU;
-	req->data[0] = 0x01; /* note: 0x00 would disable */
-
-	hid_dbg(ctlr->hdev, "enabling IMU\n");
-	return joycon_send_subcmd(ctlr, req, 1, HZ);
-}
-
-static s32 joycon_map_stick_val(struct joycon_stick_cal *cal, s32 val)
-{
-	s32 center = cal->center;
-	s32 min = cal->min;
-	s32 max = cal->max;
-	s32 new_val;
-
-	if (val > center) {
-		new_val = (val - center) * JC_MAX_STICK_MAG;
-		new_val /= (max - center);
-	} else {
-		new_val = (center - val) * -JC_MAX_STICK_MAG;
-		new_val /= (center - min);
-	}
-	new_val = clamp(new_val, (s32)-JC_MAX_STICK_MAG, (s32)JC_MAX_STICK_MAG);
-	return new_val;
-}
-
-static void joycon_input_report_parse_imu_data(struct joycon_ctlr *ctlr,
-					       struct joycon_input_report *rep,
-					       struct joycon_imu_data *imu_data)
-{
-	u8 *raw = rep->imu_raw_bytes;
-	int i;
-
-	for (i = 0; i < 3; i++) {
-		struct joycon_imu_data *data = &imu_data[i];
-
-		data->accel_x = get_unaligned_le16(raw + 0);
-		data->accel_y = get_unaligned_le16(raw + 2);
-		data->accel_z = get_unaligned_le16(raw + 4);
-		data->gyro_x = get_unaligned_le16(raw + 6);
-		data->gyro_y = get_unaligned_le16(raw + 8);
-		data->gyro_z = get_unaligned_le16(raw + 10);
-		/* point to next imu sample */
-		raw += sizeof(struct joycon_imu_data);
-	}
-}
-
-static void joycon_parse_imu_report(struct joycon_ctlr *ctlr,
-				    struct joycon_input_report *rep)
-{
-	struct joycon_imu_data imu_data[3] = {0}; /* 3 reports per packet */
-	struct input_dev *idev = ctlr->imu_input;
-	unsigned int msecs = jiffies_to_msecs(jiffies);
-	unsigned int last_msecs = ctlr->imu_last_pkt_ms;
-	int i;
-	int value[6];
-
-	joycon_input_report_parse_imu_data(ctlr, rep, imu_data);
-
-	/*
-	 * There are complexities surrounding how we determine the timestamps we
-	 * associate with the samples we pass to userspace. The IMU input
-	 * reports do not provide us with a good timestamp. There's a quickly
-	 * incrementing 8-bit counter per input report, but it is not very
-	 * useful for this purpose (it is not entirely clear what rate it
-	 * increments at or if it varies based on packet push rate - more on
-	 * the push rate below...).
-	 *
-	 * The reverse engineering work done on the joy-cons and pro controllers
-	 * by the community seems to indicate the following:
-	 * - The controller samples the IMU every 1.35ms. It then does some of
-	 *   its own processing, probably averaging the samples out.
-	 * - Each imu input report contains 3 IMU samples, (usually 5ms apart).
-	 * - In the standard reporting mode (which this driver uses exclusively)
-	 *   input reports are pushed from the controller as follows:
-	 *      * joy-con (bluetooth): every 15 ms
-	 *      * joy-cons (in charging grip via USB): every 15 ms
-	 *      * pro controller (USB): every 15 ms
-	 *      * pro controller (bluetooth): every 8 ms (this is the wildcard)
-	 *
-	 * Further complicating matters is that some bluetooth stacks are known
-	 * to alter the controller's packet rate by hardcoding the bluetooth
-	 * SSR for the switch controllers (android's stack currently sets the
-	 * SSR to 11ms for both the joy-cons and pro controllers).
-	 *
-	 * In my own testing, I've discovered that my pro controller either
-	 * reports IMU sample batches every 11ms or every 15ms. This rate is
-	 * stable after connecting. It isn't 100% clear what determines this
-	 * rate. Importantly, even when sending every 11ms, none of the samples
-	 * are duplicates. This seems to indicate that the time deltas between
-	 * reported samples can vary based on the input report rate.
-	 *
-	 * The solution employed in this driver is to keep track of the average
-	 * time delta between IMU input reports. In testing, this value has
-	 * proven to be stable, staying at 15ms or 11ms, though other hardware
-	 * configurations and bluetooth stacks could potentially see other rates
-	 * (hopefully this will become more clear as more people use the
-	 * driver).
-	 *
-	 * Keeping track of the average report delta allows us to submit our
-	 * timestamps to userspace based on that. Each report contains 3
-	 * samples, so the IMU sampling rate should be avg_time_delta/3. We can
-	 * also use this average to detect events where we have dropped a
-	 * packet. The userspace timestamp for the samples will be adjusted
-	 * accordingly to prevent unwanted behvaior.
-	 */
-	if (!ctlr->imu_first_packet_received) {
-		ctlr->imu_timestamp_us = 0;
-		ctlr->imu_delta_samples_count = 0;
-		ctlr->imu_delta_samples_sum = 0;
-		ctlr->imu_avg_delta_ms = JC_IMU_DFLT_AVG_DELTA_MS;
-		ctlr->imu_first_packet_received = true;
-	} else {
-		unsigned int delta = msecs - last_msecs;
-		unsigned int dropped_pkts;
-		unsigned int dropped_threshold;
-
-		/* avg imu report delta housekeeping */
-		ctlr->imu_delta_samples_sum += delta;
-		ctlr->imu_delta_samples_count++;
-		if (ctlr->imu_delta_samples_count >=
-		    JC_IMU_SAMPLES_PER_DELTA_AVG) {
-			ctlr->imu_avg_delta_ms = ctlr->imu_delta_samples_sum /
-						 ctlr->imu_delta_samples_count;
-			/* don't ever want divide by zero shenanigans */
-			if (ctlr->imu_avg_delta_ms == 0) {
-				ctlr->imu_avg_delta_ms = 1;
-				hid_warn(ctlr->hdev,
-					 "calculated avg imu delta of 0\n");
-			}
-			ctlr->imu_delta_samples_count = 0;
-			ctlr->imu_delta_samples_sum = 0;
-		}
-
-		/* useful for debugging IMU sample rate */
-		hid_dbg(ctlr->hdev,
-			"imu_report: ms=%u last_ms=%u delta=%u avg_delta=%u\n",
-			msecs, last_msecs, delta, ctlr->imu_avg_delta_ms);
-
-		/* check if any packets have been dropped */
-		dropped_threshold = ctlr->imu_avg_delta_ms * 3 / 2;
-		dropped_pkts = (delta - min(delta, dropped_threshold)) /
-				ctlr->imu_avg_delta_ms;
-		ctlr->imu_timestamp_us += 1000 * ctlr->imu_avg_delta_ms;
-		if (dropped_pkts > JC_IMU_DROPPED_PKT_WARNING) {
-			hid_warn(ctlr->hdev,
-				 "compensating for %u dropped IMU reports\n",
-				 dropped_pkts);
-			hid_warn(ctlr->hdev,
-				 "delta=%u avg_delta=%u\n",
-				 delta, ctlr->imu_avg_delta_ms);
-		}
-	}
-	ctlr->imu_last_pkt_ms = msecs;
-
-	/* Each IMU input report contains three samples */
-	for (i = 0; i < 3; i++) {
-		input_event(idev, EV_MSC, MSC_TIMESTAMP,
-			    ctlr->imu_timestamp_us);
-
-		/*
-		 * These calculations (which use the controller's calibration
-		 * settings to improve the final values) are based on those
-		 * found in the community's reverse-engineering repo (linked at
-		 * top of driver). For hid-nintendo, we make sure that the final
-		 * value given to userspace is always in terms of the axis
-		 * resolution we provided.
-		 *
-		 * Currently only the gyro calculations subtract the calibration
-		 * offsets from the raw value itself. In testing, doing the same
-		 * for the accelerometer raw values decreased accuracy.
-		 *
-		 * Note that the gyro values are multiplied by the
-		 * precision-saving scaling factor to prevent large inaccuracies
-		 * due to truncation of the resolution value which would
-		 * otherwise occur. To prevent overflow (without resorting to 64
-		 * bit integer math), the mult_frac macro is used.
-		 */
-		value[0] = mult_frac((JC_IMU_PREC_RANGE_SCALE *
-				      (imu_data[i].gyro_x -
-				       ctlr->gyro_cal.offset[0])),
-				     ctlr->gyro_cal.scale[0],
-				     ctlr->imu_cal_gyro_divisor[0]);
-		value[1] = mult_frac((JC_IMU_PREC_RANGE_SCALE *
-				      (imu_data[i].gyro_y -
-				       ctlr->gyro_cal.offset[1])),
-				     ctlr->gyro_cal.scale[1],
-				     ctlr->imu_cal_gyro_divisor[1]);
-		value[2] = mult_frac((JC_IMU_PREC_RANGE_SCALE *
-				      (imu_data[i].gyro_z -
-				       ctlr->gyro_cal.offset[2])),
-				     ctlr->gyro_cal.scale[2],
-				     ctlr->imu_cal_gyro_divisor[2]);
-
-		value[3] = ((s32)imu_data[i].accel_x *
-			    ctlr->accel_cal.scale[0]) /
-			    ctlr->imu_cal_accel_divisor[0];
-		value[4] = ((s32)imu_data[i].accel_y *
-			    ctlr->accel_cal.scale[1]) /
-			    ctlr->imu_cal_accel_divisor[1];
-		value[5] = ((s32)imu_data[i].accel_z *
-			    ctlr->accel_cal.scale[2]) /
-			    ctlr->imu_cal_accel_divisor[2];
-
-		hid_dbg(ctlr->hdev, "raw_gyro: g_x=%d g_y=%d g_z=%d\n",
-			imu_data[i].gyro_x, imu_data[i].gyro_y,
-			imu_data[i].gyro_z);
-		hid_dbg(ctlr->hdev, "raw_accel: a_x=%d a_y=%d a_z=%d\n",
-			imu_data[i].accel_x, imu_data[i].accel_y,
-			imu_data[i].accel_z);
-
-		/*
-		 * The right joy-con has 2 axes negated, Y and Z. This is due to
-		 * the orientation of the IMU in the controller. We negate those
-		 * axes' values in order to be consistent with the left joy-con
-		 * and the pro controller:
-		 *   X: positive is pointing toward the triggers
-		 *   Y: positive is pointing to the left
-		 *   Z: positive is pointing up (out of the buttons/sticks)
-		 * The axes follow the right-hand rule.
-		 */
-		if (jc_type_is_joycon(ctlr) && jc_type_has_right(ctlr)) {
-			int j;
-
-			/* negate all but x axis */
-			for (j = 1; j < 6; ++j) {
-				if (j == 3)
-					continue;
-				value[j] *= -1;
-			}
-		}
-
-		input_report_abs(idev, ABS_RX, value[0]);
-		input_report_abs(idev, ABS_RY, value[1]);
-		input_report_abs(idev, ABS_RZ, value[2]);
-		input_report_abs(idev, ABS_X, value[3]);
-		input_report_abs(idev, ABS_Y, value[4]);
-		input_report_abs(idev, ABS_Z, value[5]);
-		input_sync(idev);
-		/* convert to micros and divide by 3 (3 samples per report). */
-		ctlr->imu_timestamp_us += ctlr->imu_avg_delta_ms * 1000 / 3;
-	}
-}
-
-static void joycon_parse_report(struct joycon_ctlr *ctlr,
-				struct joycon_input_report *rep)
-{
-	struct input_dev *dev = ctlr->input;
-	unsigned long flags;
-	u8 tmp;
-	u32 btns;
-	unsigned long msecs = jiffies_to_msecs(jiffies);
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	if (IS_ENABLED(CONFIG_NINTENDO_FF) && rep->vibrator_report &&
-	    (msecs - ctlr->rumble_msecs) >= JC_RUMBLE_PERIOD_MS &&
-	    (ctlr->rumble_queue_head != ctlr->rumble_queue_tail ||
-	     ctlr->rumble_zero_countdown > 0)) {
-		/*
-		 * When this value reaches 0, we know we've sent multiple
-		 * packets to the controller instructing it to disable rumble.
-		 * We can safely stop sending periodic rumble packets until the
-		 * next ff effect.
-		 */
-		if (ctlr->rumble_zero_countdown > 0)
-			ctlr->rumble_zero_countdown--;
-		queue_work(ctlr->rumble_queue, &ctlr->rumble_worker);
-	}
-
-	/* Parse the battery status */
-	tmp = rep->bat_con;
-	ctlr->host_powered = tmp & BIT(0);
-	ctlr->battery_charging = tmp & BIT(4);
-	tmp = tmp >> 5;
-	switch (tmp) {
-	case 0: /* empty */
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
-		break;
-	case 1: /* low */
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-		break;
-	case 2: /* medium */
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-		break;
-	case 3: /* high */
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
-		break;
-	case 4: /* full */
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-		break;
-	default:
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
-		hid_warn(ctlr->hdev, "Invalid battery status\n");
-		break;
-	}
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	/* Parse the buttons and sticks */
-	btns = hid_field_extract(ctlr->hdev, rep->button_status, 0, 24);
-
-	if (jc_type_has_left(ctlr)) {
-		u16 raw_x;
-		u16 raw_y;
-		s32 x;
-		s32 y;
-
-		/* get raw stick values */
-		raw_x = hid_field_extract(ctlr->hdev, rep->left_stick, 0, 12);
-		raw_y = hid_field_extract(ctlr->hdev,
-					  rep->left_stick + 1, 4, 12);
-		/* map the stick values */
-		x = joycon_map_stick_val(&ctlr->left_stick_cal_x, raw_x);
-		y = -joycon_map_stick_val(&ctlr->left_stick_cal_y, raw_y);
-		/* report sticks */
-		input_report_abs(dev, ABS_X, x);
-		input_report_abs(dev, ABS_Y, y);
-
-		/* report buttons */
-		input_report_key(dev, BTN_TL, btns & JC_BTN_L);
-		input_report_key(dev, BTN_TL2, btns & JC_BTN_ZL);
-		input_report_key(dev, BTN_SELECT, btns & JC_BTN_MINUS);
-		input_report_key(dev, BTN_THUMBL, btns & JC_BTN_LSTICK);
-		input_report_key(dev, BTN_Z, btns & JC_BTN_CAP);
-
-		if (jc_type_is_joycon(ctlr)) {
-			/* Report the S buttons as the non-existent triggers */
-			input_report_key(dev, BTN_TR, btns & JC_BTN_SL_L);
-			input_report_key(dev, BTN_TR2, btns & JC_BTN_SR_L);
-
-			/* Report d-pad as digital buttons for the joy-cons */
-			input_report_key(dev, BTN_DPAD_DOWN,
-					 btns & JC_BTN_DOWN);
-			input_report_key(dev, BTN_DPAD_UP, btns & JC_BTN_UP);
-			input_report_key(dev, BTN_DPAD_RIGHT,
-					 btns & JC_BTN_RIGHT);
-			input_report_key(dev, BTN_DPAD_LEFT,
-					 btns & JC_BTN_LEFT);
-		} else {
-			int hatx = 0;
-			int haty = 0;
-
-			/* d-pad x */
-			if (btns & JC_BTN_LEFT)
-				hatx = -1;
-			else if (btns & JC_BTN_RIGHT)
-				hatx = 1;
-			input_report_abs(dev, ABS_HAT0X, hatx);
-
-			/* d-pad y */
-			if (btns & JC_BTN_UP)
-				haty = -1;
-			else if (btns & JC_BTN_DOWN)
-				haty = 1;
-			input_report_abs(dev, ABS_HAT0Y, haty);
-		}
-	}
-	if (jc_type_has_right(ctlr)) {
-		u16 raw_x;
-		u16 raw_y;
-		s32 x;
-		s32 y;
-
-		/* get raw stick values */
-		raw_x = hid_field_extract(ctlr->hdev, rep->right_stick, 0, 12);
-		raw_y = hid_field_extract(ctlr->hdev,
-					  rep->right_stick + 1, 4, 12);
-		/* map stick values */
-		x = joycon_map_stick_val(&ctlr->right_stick_cal_x, raw_x);
-		y = -joycon_map_stick_val(&ctlr->right_stick_cal_y, raw_y);
-		/* report sticks */
-		input_report_abs(dev, ABS_RX, x);
-		input_report_abs(dev, ABS_RY, y);
-
-		/* report buttons */
-		input_report_key(dev, BTN_TR, btns & JC_BTN_R);
-		input_report_key(dev, BTN_TR2, btns & JC_BTN_ZR);
-		if (jc_type_is_joycon(ctlr)) {
-			/* Report the S buttons as the non-existent triggers */
-			input_report_key(dev, BTN_TL, btns & JC_BTN_SL_R);
-			input_report_key(dev, BTN_TL2, btns & JC_BTN_SR_R);
-		}
-		input_report_key(dev, BTN_START, btns & JC_BTN_PLUS);
-		input_report_key(dev, BTN_THUMBR, btns & JC_BTN_RSTICK);
-		input_report_key(dev, BTN_MODE, btns & JC_BTN_HOME);
-		input_report_key(dev, BTN_WEST, btns & JC_BTN_Y);
-		input_report_key(dev, BTN_NORTH, btns & JC_BTN_X);
-		input_report_key(dev, BTN_EAST, btns & JC_BTN_A);
-		input_report_key(dev, BTN_SOUTH, btns & JC_BTN_B);
-	}
-
-	input_sync(dev);
-
-	/*
-	 * Immediately after receiving a report is the most reliable time to
-	 * send a subcommand to the controller. Wake any subcommand senders
-	 * waiting for a report.
-	 */
-	if (unlikely(mutex_is_locked(&ctlr->output_mutex))) {
-		spin_lock_irqsave(&ctlr->lock, flags);
-		ctlr->received_input_report = true;
-		spin_unlock_irqrestore(&ctlr->lock, flags);
-		wake_up(&ctlr->wait);
-	}
-
-	/* parse IMU data if present */
-	if (rep->id == JC_INPUT_IMU_DATA)
-		joycon_parse_imu_report(ctlr, rep);
-}
-
-static int joycon_send_rumble_data(struct joycon_ctlr *ctlr)
-{
-	int ret;
-	unsigned long flags;
-	struct joycon_rumble_output rumble_output = { 0 };
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	/*
-	 * If the controller has been removed, just return ENODEV so the LED
-	 * subsystem doesn't print invalid errors on removal.
-	 */
-	if (ctlr->ctlr_state == JOYCON_CTLR_STATE_REMOVED) {
-		spin_unlock_irqrestore(&ctlr->lock, flags);
-		return -ENODEV;
-	}
-	memcpy(rumble_output.rumble_data,
-	       ctlr->rumble_data[ctlr->rumble_queue_tail],
-	       JC_RUMBLE_DATA_SIZE);
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	rumble_output.output_id = JC_OUTPUT_RUMBLE_ONLY;
-	rumble_output.packet_num = ctlr->subcmd_num;
-	if (++ctlr->subcmd_num > 0xF)
-		ctlr->subcmd_num = 0;
-
-	joycon_enforce_subcmd_rate(ctlr);
-
-	ret = __joycon_hid_send(ctlr->hdev, (u8 *)&rumble_output,
-				sizeof(rumble_output));
-	return ret;
-}
-
-static void joycon_rumble_worker(struct work_struct *work)
-{
-	struct joycon_ctlr *ctlr = container_of(work, struct joycon_ctlr,
-							rumble_worker);
-	unsigned long flags;
-	bool again = true;
-	int ret;
-
-	while (again) {
-		mutex_lock(&ctlr->output_mutex);
-		ret = joycon_send_rumble_data(ctlr);
-		mutex_unlock(&ctlr->output_mutex);
-
-		/* -ENODEV means the controller was just unplugged */
-		spin_lock_irqsave(&ctlr->lock, flags);
-		if (ret < 0 && ret != -ENODEV &&
-		    ctlr->ctlr_state != JOYCON_CTLR_STATE_REMOVED)
-			hid_warn(ctlr->hdev, "Failed to set rumble; e=%d", ret);
-
-		ctlr->rumble_msecs = jiffies_to_msecs(jiffies);
-		if (ctlr->rumble_queue_tail != ctlr->rumble_queue_head) {
-			if (++ctlr->rumble_queue_tail >= JC_RUMBLE_QUEUE_SIZE)
-				ctlr->rumble_queue_tail = 0;
-		} else {
-			again = false;
-		}
-		spin_unlock_irqrestore(&ctlr->lock, flags);
-	}
-}
-
-#if IS_ENABLED(CONFIG_NINTENDO_FF)
-static struct joycon_rumble_freq_data joycon_find_rumble_freq(u16 freq)
-{
-	const size_t length = ARRAY_SIZE(joycon_rumble_frequencies);
-	const struct joycon_rumble_freq_data *data = joycon_rumble_frequencies;
-	int i = 0;
-
-	if (freq > data[0].freq) {
-		for (i = 1; i < length - 1; i++) {
-			if (freq > data[i - 1].freq && freq <= data[i].freq)
-				break;
-		}
-	}
-
-	return data[i];
-}
-
-static struct joycon_rumble_amp_data joycon_find_rumble_amp(u16 amp)
-{
-	const size_t length = ARRAY_SIZE(joycon_rumble_amplitudes);
-	const struct joycon_rumble_amp_data *data = joycon_rumble_amplitudes;
-	int i = 0;
-
-	if (amp > data[0].amp) {
-		for (i = 1; i < length - 1; i++) {
-			if (amp > data[i - 1].amp && amp <= data[i].amp)
-				break;
-		}
-	}
-
-	return data[i];
-}
-
-static void joycon_encode_rumble(u8 *data, u16 freq_low, u16 freq_high, u16 amp)
-{
-	struct joycon_rumble_freq_data freq_data_low;
-	struct joycon_rumble_freq_data freq_data_high;
-	struct joycon_rumble_amp_data amp_data;
-
-	freq_data_low = joycon_find_rumble_freq(freq_low);
-	freq_data_high = joycon_find_rumble_freq(freq_high);
-	amp_data = joycon_find_rumble_amp(amp);
-
-	data[0] = (freq_data_high.high >> 8) & 0xFF;
-	data[1] = (freq_data_high.high & 0xFF) + amp_data.high;
-	data[2] = freq_data_low.low + ((amp_data.low >> 8) & 0xFF);
-	data[3] = amp_data.low & 0xFF;
-}
-
-static const u16 JOYCON_MAX_RUMBLE_HIGH_FREQ	= 1253;
-static const u16 JOYCON_MIN_RUMBLE_HIGH_FREQ	= 82;
-static const u16 JOYCON_MAX_RUMBLE_LOW_FREQ	= 626;
-static const u16 JOYCON_MIN_RUMBLE_LOW_FREQ	= 41;
-
-static void joycon_clamp_rumble_freqs(struct joycon_ctlr *ctlr)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	ctlr->rumble_ll_freq = clamp(ctlr->rumble_ll_freq,
-				     JOYCON_MIN_RUMBLE_LOW_FREQ,
-				     JOYCON_MAX_RUMBLE_LOW_FREQ);
-	ctlr->rumble_lh_freq = clamp(ctlr->rumble_lh_freq,
-				     JOYCON_MIN_RUMBLE_HIGH_FREQ,
-				     JOYCON_MAX_RUMBLE_HIGH_FREQ);
-	ctlr->rumble_rl_freq = clamp(ctlr->rumble_rl_freq,
-				     JOYCON_MIN_RUMBLE_LOW_FREQ,
-				     JOYCON_MAX_RUMBLE_LOW_FREQ);
-	ctlr->rumble_rh_freq = clamp(ctlr->rumble_rh_freq,
-				     JOYCON_MIN_RUMBLE_HIGH_FREQ,
-				     JOYCON_MAX_RUMBLE_HIGH_FREQ);
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-}
-
-static int joycon_set_rumble(struct joycon_ctlr *ctlr, u16 amp_r, u16 amp_l,
-			     bool schedule_now)
-{
-	u8 data[JC_RUMBLE_DATA_SIZE];
-	u16 amp;
-	u16 freq_r_low;
-	u16 freq_r_high;
-	u16 freq_l_low;
-	u16 freq_l_high;
-	unsigned long flags;
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	freq_r_low = ctlr->rumble_rl_freq;
-	freq_r_high = ctlr->rumble_rh_freq;
-	freq_l_low = ctlr->rumble_ll_freq;
-	freq_l_high = ctlr->rumble_lh_freq;
-	/* limit number of silent rumble packets to reduce traffic */
-	if (amp_l != 0 || amp_r != 0)
-		ctlr->rumble_zero_countdown = JC_RUMBLE_ZERO_AMP_PKT_CNT;
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	/* right joy-con */
-	amp = amp_r * (u32)joycon_max_rumble_amp / 65535;
-	joycon_encode_rumble(data + 4, freq_r_low, freq_r_high, amp);
-
-	/* left joy-con */
-	amp = amp_l * (u32)joycon_max_rumble_amp / 65535;
-	joycon_encode_rumble(data, freq_l_low, freq_l_high, amp);
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	if (++ctlr->rumble_queue_head >= JC_RUMBLE_QUEUE_SIZE)
-		ctlr->rumble_queue_head = 0;
-	memcpy(ctlr->rumble_data[ctlr->rumble_queue_head], data,
-	       JC_RUMBLE_DATA_SIZE);
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	/* don't wait for the periodic send (reduces latency) */
-	if (schedule_now)
-		queue_work(ctlr->rumble_queue, &ctlr->rumble_worker);
-
-	return 0;
-}
-
-static int joycon_play_effect(struct input_dev *dev, void *data,
-						     struct ff_effect *effect)
-{
-	struct joycon_ctlr *ctlr = input_get_drvdata(dev);
-
-	if (effect->type != FF_RUMBLE)
-		return 0;
-
-	return joycon_set_rumble(ctlr,
-				 effect->u.rumble.weak_magnitude,
-				 effect->u.rumble.strong_magnitude,
-				 true);
-}
-#endif /* IS_ENABLED(CONFIG_NINTENDO_FF) */
-
-static const unsigned int joycon_button_inputs_l[] = {
-	BTN_SELECT, BTN_Z, BTN_THUMBL,
-	BTN_TL, BTN_TL2,
-	0 /* 0 signals end of array */
-};
-
-static const unsigned int joycon_button_inputs_r[] = {
-	BTN_START, BTN_MODE, BTN_THUMBR,
-	BTN_SOUTH, BTN_EAST, BTN_NORTH, BTN_WEST,
-	BTN_TR, BTN_TR2,
-	0 /* 0 signals end of array */
-};
-
-/* We report joy-con d-pad inputs as buttons and pro controller as a hat. */
-static const unsigned int joycon_dpad_inputs_jc[] = {
-	BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT,
-};
-
-static int joycon_input_create(struct joycon_ctlr *ctlr)
-{
-	struct hid_device *hdev;
-	const char *name;
-	const char *imu_name;
-	int ret;
-	int i;
-
-	hdev = ctlr->hdev;
-
-	switch (hdev->product) {
-	case USB_DEVICE_ID_NINTENDO_PROCON:
-		name = "Nintendo Switch Pro Controller";
-		imu_name = "Nintendo Switch Pro Controller IMU";
-		break;
-	case USB_DEVICE_ID_NINTENDO_CHRGGRIP:
-		if (jc_type_has_left(ctlr)) {
-			name = "Nintendo Switch Left Joy-Con (Grip)";
-			imu_name = "Nintendo Switch Left Joy-Con IMU (Grip)";
-		} else {
-			name = "Nintendo Switch Right Joy-Con (Grip)";
-			imu_name = "Nintendo Switch Right Joy-Con IMU (Grip)";
-		}
-		break;
-	case USB_DEVICE_ID_NINTENDO_JOYCONL:
-		name = "Nintendo Switch Left Joy-Con";
-		imu_name = "Nintendo Switch Left Joy-Con IMU";
-		break;
-	case USB_DEVICE_ID_NINTENDO_JOYCONR:
-		name = "Nintendo Switch Right Joy-Con";
-		imu_name = "Nintendo Switch Right Joy-Con IMU";
-		break;
-	default: /* Should be impossible */
-		hid_err(hdev, "Invalid hid product\n");
-		return -EINVAL;
-	}
-
-	ctlr->input = devm_input_allocate_device(&hdev->dev);
-	if (!ctlr->input)
-		return -ENOMEM;
-	ctlr->input->id.bustype = hdev->bus;
-	ctlr->input->id.vendor = hdev->vendor;
-	ctlr->input->id.product = hdev->product;
-	ctlr->input->id.version = hdev->version;
-	ctlr->input->uniq = ctlr->mac_addr_str;
-	ctlr->input->name = name;
-	input_set_drvdata(ctlr->input, ctlr);
-
-	/* set up sticks and buttons */
-	if (jc_type_has_left(ctlr)) {
-		input_set_abs_params(ctlr->input, ABS_X,
-				     -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
-				     JC_STICK_FUZZ, JC_STICK_FLAT);
-		input_set_abs_params(ctlr->input, ABS_Y,
-				     -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
-				     JC_STICK_FUZZ, JC_STICK_FLAT);
-
-		for (i = 0; joycon_button_inputs_l[i] > 0; i++)
-			input_set_capability(ctlr->input, EV_KEY,
-					     joycon_button_inputs_l[i]);
-
-		/* configure d-pad differently for joy-con vs pro controller */
-		if (hdev->product != USB_DEVICE_ID_NINTENDO_PROCON) {
-			for (i = 0; joycon_dpad_inputs_jc[i] > 0; i++)
-				input_set_capability(ctlr->input, EV_KEY,
-						     joycon_dpad_inputs_jc[i]);
-		} else {
-			input_set_abs_params(ctlr->input, ABS_HAT0X,
-					     -JC_MAX_DPAD_MAG, JC_MAX_DPAD_MAG,
-					     JC_DPAD_FUZZ, JC_DPAD_FLAT);
-			input_set_abs_params(ctlr->input, ABS_HAT0Y,
-					     -JC_MAX_DPAD_MAG, JC_MAX_DPAD_MAG,
-					     JC_DPAD_FUZZ, JC_DPAD_FLAT);
-		}
-	}
-	if (jc_type_has_right(ctlr)) {
-		input_set_abs_params(ctlr->input, ABS_RX,
-				     -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
-				     JC_STICK_FUZZ, JC_STICK_FLAT);
-		input_set_abs_params(ctlr->input, ABS_RY,
-				     -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
-				     JC_STICK_FUZZ, JC_STICK_FLAT);
-
-		for (i = 0; joycon_button_inputs_r[i] > 0; i++)
-			input_set_capability(ctlr->input, EV_KEY,
-					     joycon_button_inputs_r[i]);
-	}
-
-	/* Let's report joy-con S triggers separately */
-	if (hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONL) {
-		input_set_capability(ctlr->input, EV_KEY, BTN_TR);
-		input_set_capability(ctlr->input, EV_KEY, BTN_TR2);
-	} else if (hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONR) {
-		input_set_capability(ctlr->input, EV_KEY, BTN_TL);
-		input_set_capability(ctlr->input, EV_KEY, BTN_TL2);
-	}
-
-#if IS_ENABLED(CONFIG_NINTENDO_FF)
-	/* set up rumble */
-	input_set_capability(ctlr->input, EV_FF, FF_RUMBLE);
-	input_ff_create_memless(ctlr->input, NULL, joycon_play_effect);
-	ctlr->rumble_ll_freq = JC_RUMBLE_DFLT_LOW_FREQ;
-	ctlr->rumble_lh_freq = JC_RUMBLE_DFLT_HIGH_FREQ;
-	ctlr->rumble_rl_freq = JC_RUMBLE_DFLT_LOW_FREQ;
-	ctlr->rumble_rh_freq = JC_RUMBLE_DFLT_HIGH_FREQ;
-	joycon_clamp_rumble_freqs(ctlr);
-	joycon_set_rumble(ctlr, 0, 0, false);
-	ctlr->rumble_msecs = jiffies_to_msecs(jiffies);
-#endif
-
-	ret = input_register_device(ctlr->input);
-	if (ret)
-		return ret;
-
-	/* configure the imu input device */
-	ctlr->imu_input = devm_input_allocate_device(&hdev->dev);
-	if (!ctlr->imu_input)
-		return -ENOMEM;
-
-	ctlr->imu_input->id.bustype = hdev->bus;
-	ctlr->imu_input->id.vendor = hdev->vendor;
-	ctlr->imu_input->id.product = hdev->product;
-	ctlr->imu_input->id.version = hdev->version;
-	ctlr->imu_input->uniq = ctlr->mac_addr_str;
-	ctlr->imu_input->name = imu_name;
-	input_set_drvdata(ctlr->imu_input, ctlr);
-
-	/* configure imu axes */
-	input_set_abs_params(ctlr->imu_input, ABS_X,
-			     -JC_IMU_MAX_ACCEL_MAG, JC_IMU_MAX_ACCEL_MAG,
-			     JC_IMU_ACCEL_FUZZ, JC_IMU_ACCEL_FLAT);
-	input_set_abs_params(ctlr->imu_input, ABS_Y,
-			     -JC_IMU_MAX_ACCEL_MAG, JC_IMU_MAX_ACCEL_MAG,
-			     JC_IMU_ACCEL_FUZZ, JC_IMU_ACCEL_FLAT);
-	input_set_abs_params(ctlr->imu_input, ABS_Z,
-			     -JC_IMU_MAX_ACCEL_MAG, JC_IMU_MAX_ACCEL_MAG,
-			     JC_IMU_ACCEL_FUZZ, JC_IMU_ACCEL_FLAT);
-	input_abs_set_res(ctlr->imu_input, ABS_X, JC_IMU_ACCEL_RES_PER_G);
-	input_abs_set_res(ctlr->imu_input, ABS_Y, JC_IMU_ACCEL_RES_PER_G);
-	input_abs_set_res(ctlr->imu_input, ABS_Z, JC_IMU_ACCEL_RES_PER_G);
-
-	input_set_abs_params(ctlr->imu_input, ABS_RX,
-			     -JC_IMU_MAX_GYRO_MAG, JC_IMU_MAX_GYRO_MAG,
-			     JC_IMU_GYRO_FUZZ, JC_IMU_GYRO_FLAT);
-	input_set_abs_params(ctlr->imu_input, ABS_RY,
-			     -JC_IMU_MAX_GYRO_MAG, JC_IMU_MAX_GYRO_MAG,
-			     JC_IMU_GYRO_FUZZ, JC_IMU_GYRO_FLAT);
-	input_set_abs_params(ctlr->imu_input, ABS_RZ,
-			     -JC_IMU_MAX_GYRO_MAG, JC_IMU_MAX_GYRO_MAG,
-			     JC_IMU_GYRO_FUZZ, JC_IMU_GYRO_FLAT);
-
-	input_abs_set_res(ctlr->imu_input, ABS_RX, JC_IMU_GYRO_RES_PER_DPS);
-	input_abs_set_res(ctlr->imu_input, ABS_RY, JC_IMU_GYRO_RES_PER_DPS);
-	input_abs_set_res(ctlr->imu_input, ABS_RZ, JC_IMU_GYRO_RES_PER_DPS);
-
-	__set_bit(EV_MSC, ctlr->imu_input->evbit);
-	__set_bit(MSC_TIMESTAMP, ctlr->imu_input->mscbit);
-	__set_bit(INPUT_PROP_ACCELEROMETER, ctlr->imu_input->propbit);
-
-	ret = input_register_device(ctlr->imu_input);
-	if (ret)
-		return ret;
-
-	return 0;
-}
-
-static int joycon_player_led_brightness_set(struct led_classdev *led,
-					    enum led_brightness brightness)
-{
-	struct device *dev = led->dev->parent;
-	struct hid_device *hdev = to_hid_device(dev);
-	struct joycon_ctlr *ctlr;
-	int val = 0;
-	int i;
-	int ret;
-	int num;
-
-	ctlr = hid_get_drvdata(hdev);
-	if (!ctlr) {
-		hid_err(hdev, "No controller data\n");
-		return -ENODEV;
-	}
-
-	/* determine which player led this is */
-	for (num = 0; num < JC_NUM_LEDS; num++) {
-		if (&ctlr->leds[num] == led)
-			break;
-	}
-	if (num >= JC_NUM_LEDS)
-		return -EINVAL;
-
-	mutex_lock(&ctlr->output_mutex);
-	for (i = 0; i < JC_NUM_LEDS; i++) {
-		if (i == num)
-			val |= brightness << i;
-		else
-			val |= ctlr->leds[i].brightness << i;
-	}
-	ret = joycon_set_player_leds(ctlr, 0, val);
-	mutex_unlock(&ctlr->output_mutex);
-
-	return ret;
-}
-
-static int joycon_home_led_brightness_set(struct led_classdev *led,
-					  enum led_brightness brightness)
-{
-	struct device *dev = led->dev->parent;
-	struct hid_device *hdev = to_hid_device(dev);
-	struct joycon_ctlr *ctlr;
-	struct joycon_subcmd_request *req;
-	u8 buffer[sizeof(*req) + 5] = { 0 };
-	u8 *data;
-	int ret;
-
-	ctlr = hid_get_drvdata(hdev);
-	if (!ctlr) {
-		hid_err(hdev, "No controller data\n");
-		return -ENODEV;
-	}
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_SET_HOME_LIGHT;
-	data = req->data;
-	data[0] = 0x01;
-	data[1] = brightness << 4;
-	data[2] = brightness | (brightness << 4);
-	data[3] = 0x11;
-	data[4] = 0x11;
-
-	hid_dbg(hdev, "setting home led brightness\n");
-	mutex_lock(&ctlr->output_mutex);
-	ret = joycon_send_subcmd(ctlr, req, 5, HZ/4);
-	mutex_unlock(&ctlr->output_mutex);
-
-	return ret;
-}
-
-static DEFINE_MUTEX(joycon_input_num_mutex);
-static int joycon_leds_create(struct joycon_ctlr *ctlr)
-{
-	struct hid_device *hdev = ctlr->hdev;
-	struct device *dev = &hdev->dev;
-	const char *d_name = dev_name(dev);
-	struct led_classdev *led;
-	char *name;
-	int ret = 0;
-	int i;
-	static int input_num = 1;
-
-	/* Set the default controller player leds based on controller number */
-	mutex_lock(&joycon_input_num_mutex);
-	mutex_lock(&ctlr->output_mutex);
-	ret = joycon_set_player_leds(ctlr, 0, 0xF >> (4 - input_num));
-	if (ret)
-		hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret);
-	mutex_unlock(&ctlr->output_mutex);
-
-	/* configure the player LEDs */
-	for (i = 0; i < JC_NUM_LEDS; i++) {
-		name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s",
-				      d_name,
-				      "green",
-				      joycon_player_led_names[i]);
-		if (!name)
-			return -ENOMEM;
-
-		led = &ctlr->leds[i];
-		led->name = name;
-		led->brightness = ((i + 1) <= input_num) ? 1 : 0;
-		led->max_brightness = 1;
-		led->brightness_set_blocking =
-					joycon_player_led_brightness_set;
-		led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
-
-		ret = devm_led_classdev_register(&hdev->dev, led);
-		if (ret) {
-			hid_err(hdev, "Failed registering %s LED\n", led->name);
-			return ret;
-		}
-	}
-
-	if (++input_num > 4)
-		input_num = 1;
-	mutex_unlock(&joycon_input_num_mutex);
-
-	/* configure the home LED */
-	if (jc_type_has_right(ctlr)) {
-		name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s",
-				      d_name,
-				      "blue",
-				      LED_FUNCTION_PLAYER5);
-		if (!name)
-			return -ENOMEM;
-
-		led = &ctlr->home_led;
-		led->name = name;
-		led->brightness = 0;
-		led->max_brightness = 0xF;
-		led->brightness_set_blocking = joycon_home_led_brightness_set;
-		led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
-		ret = devm_led_classdev_register(&hdev->dev, led);
-		if (ret) {
-			hid_err(hdev, "Failed registering home led\n");
-			return ret;
-		}
-		/* Set the home LED to 0 as default state */
-		ret = joycon_home_led_brightness_set(led, 0);
-		if (ret) {
-			hid_err(hdev, "Failed to set home LED dflt; ret=%d\n",
-									ret);
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-static int joycon_battery_get_property(struct power_supply *supply,
-				       enum power_supply_property prop,
-				       union power_supply_propval *val)
-{
-	struct joycon_ctlr *ctlr = power_supply_get_drvdata(supply);
-	unsigned long flags;
-	int ret = 0;
-	u8 capacity;
-	bool charging;
-	bool powered;
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	capacity = ctlr->battery_capacity;
-	charging = ctlr->battery_charging;
-	powered = ctlr->host_powered;
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	switch (prop) {
-	case POWER_SUPPLY_PROP_PRESENT:
-		val->intval = 1;
-		break;
-	case POWER_SUPPLY_PROP_SCOPE:
-		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
-		break;
-	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
-		val->intval = capacity;
-		break;
-	case POWER_SUPPLY_PROP_STATUS:
-		if (charging)
-			val->intval = POWER_SUPPLY_STATUS_CHARGING;
-		else if (capacity == POWER_SUPPLY_CAPACITY_LEVEL_FULL &&
-			 powered)
-			val->intval = POWER_SUPPLY_STATUS_FULL;
-		else
-			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-	return ret;
-}
-
-static enum power_supply_property joycon_battery_props[] = {
-	POWER_SUPPLY_PROP_PRESENT,
-	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-	POWER_SUPPLY_PROP_SCOPE,
-	POWER_SUPPLY_PROP_STATUS,
-};
-
-static int joycon_power_supply_create(struct joycon_ctlr *ctlr)
-{
-	struct hid_device *hdev = ctlr->hdev;
-	struct power_supply_config supply_config = { .drv_data = ctlr, };
-	const char * const name_fmt = "nintendo_switch_controller_battery_%s";
-	int ret = 0;
-
-	/* Set initially to unknown before receiving first input report */
-	ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
-
-	/* Configure the battery's description */
-	ctlr->battery_desc.properties = joycon_battery_props;
-	ctlr->battery_desc.num_properties =
-					ARRAY_SIZE(joycon_battery_props);
-	ctlr->battery_desc.get_property = joycon_battery_get_property;
-	ctlr->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
-	ctlr->battery_desc.use_for_apm = 0;
-	ctlr->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
-						 name_fmt,
-						 dev_name(&hdev->dev));
-	if (!ctlr->battery_desc.name)
-		return -ENOMEM;
-
-	ctlr->battery = devm_power_supply_register(&hdev->dev,
-						   &ctlr->battery_desc,
-						   &supply_config);
-	if (IS_ERR(ctlr->battery)) {
-		ret = PTR_ERR(ctlr->battery);
-		hid_err(hdev, "Failed to register battery; ret=%d\n", ret);
-		return ret;
-	}
-
-	return power_supply_powers(ctlr->battery, &hdev->dev);
-}
-
-static int joycon_read_info(struct joycon_ctlr *ctlr)
-{
-	int ret;
-	int i;
-	int j;
-	struct joycon_subcmd_request req = { 0 };
-	struct joycon_input_report *report;
-
-	req.subcmd_id = JC_SUBCMD_REQ_DEV_INFO;
-	ret = joycon_send_subcmd(ctlr, &req, 0, HZ);
-	if (ret) {
-		hid_err(ctlr->hdev, "Failed to get joycon info; ret=%d\n", ret);
-		return ret;
-	}
-
-	report = (struct joycon_input_report *)ctlr->input_buf;
-
-	for (i = 4, j = 0; j < 6; i++, j++)
-		ctlr->mac_addr[j] = report->subcmd_reply.data[i];
-
-	ctlr->mac_addr_str = devm_kasprintf(&ctlr->hdev->dev, GFP_KERNEL,
-					    "%02X:%02X:%02X:%02X:%02X:%02X",
-					    ctlr->mac_addr[0],
-					    ctlr->mac_addr[1],
-					    ctlr->mac_addr[2],
-					    ctlr->mac_addr[3],
-					    ctlr->mac_addr[4],
-					    ctlr->mac_addr[5]);
-	if (!ctlr->mac_addr_str)
-		return -ENOMEM;
-	hid_info(ctlr->hdev, "controller MAC = %s\n", ctlr->mac_addr_str);
-
-	/* Retrieve the type so we can distinguish for charging grip */
-	ctlr->ctlr_type = report->subcmd_reply.data[2];
-
-	return 0;
-}
-
-/* Common handler for parsing inputs */
-static int joycon_ctlr_read_handler(struct joycon_ctlr *ctlr, u8 *data,
-							      int size)
-{
-	if (data[0] == JC_INPUT_SUBCMD_REPLY || data[0] == JC_INPUT_IMU_DATA ||
-	    data[0] == JC_INPUT_MCU_DATA) {
-		if (size >= 12) /* make sure it contains the input report */
-			joycon_parse_report(ctlr,
-					    (struct joycon_input_report *)data);
-	}
-
-	return 0;
-}
-
-static int joycon_ctlr_handle_event(struct joycon_ctlr *ctlr, u8 *data,
-							      int size)
-{
-	int ret = 0;
-	bool match = false;
-	struct joycon_input_report *report;
-
-	if (unlikely(mutex_is_locked(&ctlr->output_mutex)) &&
-	    ctlr->msg_type != JOYCON_MSG_TYPE_NONE) {
-		switch (ctlr->msg_type) {
-		case JOYCON_MSG_TYPE_USB:
-			if (size < 2)
-				break;
-			if (data[0] == JC_INPUT_USB_RESPONSE &&
-			    data[1] == ctlr->usb_ack_match)
-				match = true;
-			break;
-		case JOYCON_MSG_TYPE_SUBCMD:
-			if (size < sizeof(struct joycon_input_report) ||
-			    data[0] != JC_INPUT_SUBCMD_REPLY)
-				break;
-			report = (struct joycon_input_report *)data;
-			if (report->subcmd_reply.id == ctlr->subcmd_ack_match)
-				match = true;
-			break;
-		default:
-			break;
-		}
-
-		if (match) {
-			memcpy(ctlr->input_buf, data,
-			       min(size, (int)JC_MAX_RESP_SIZE));
-			ctlr->msg_type = JOYCON_MSG_TYPE_NONE;
-			ctlr->received_resp = true;
-			wake_up(&ctlr->wait);
-
-			/* This message has been handled */
-			return 1;
-		}
-	}
-
-	if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ)
-		ret = joycon_ctlr_read_handler(ctlr, data, size);
-
-	return ret;
-}
 
 static int nintendo_hid_event(struct hid_device *hdev,
 			      struct hid_report *report, u8 *raw_data, int size)
 {
-	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
-
-	if (size < 1)
-		return -EINVAL;
+	enum nintendo_driver *driver = hid_get_drvdata(hdev);
 
-	return joycon_ctlr_handle_event(ctlr, raw_data, size);
+#ifdef CONFIG_HID_NINTENDO_SWITCH
+	if (*driver == NINTENDO_SWITCH)
+		return switch_hid_event(hdev, report, raw_data, size);
+#endif
+	unreachable();
 }
 
 static int nintendo_hid_probe(struct hid_device *hdev,
-			    const struct hid_device_id *id)
+			      const struct hid_device_id *id)
 {
 	int ret;
-	struct joycon_ctlr *ctlr;
-
-	hid_dbg(hdev, "probe - start\n");
-
-	ctlr = devm_kzalloc(&hdev->dev, sizeof(*ctlr), GFP_KERNEL);
-	if (!ctlr) {
-		ret = -ENOMEM;
-		goto err;
-	}
 
-	ctlr->hdev = hdev;
-	ctlr->ctlr_state = JOYCON_CTLR_STATE_INIT;
-	ctlr->rumble_queue_head = JC_RUMBLE_QUEUE_SIZE - 1;
-	ctlr->rumble_queue_tail = 0;
-	hid_set_drvdata(hdev, ctlr);
-	mutex_init(&ctlr->output_mutex);
-	init_waitqueue_head(&ctlr->wait);
-	spin_lock_init(&ctlr->lock);
-	ctlr->rumble_queue = alloc_workqueue("hid-nintendo-rumble_wq",
-					     WQ_FREEZABLE | WQ_MEM_RECLAIM, 0);
-	INIT_WORK(&ctlr->rumble_worker, joycon_rumble_worker);
-
-	ret = hid_parse(hdev);
-	if (ret) {
-		hid_err(hdev, "HID parse failed\n");
-		goto err_wq;
-	}
-
-	/*
-	 * Patch the hw version of pro controller/joycons, so applications can
-	 * distinguish between the default HID mappings and the mappings defined
-	 * by the Linux game controller spec. This is important for the SDL2
-	 * library, which has a game controller database, which uses device ids
-	 * in combination with version as a key.
-	 */
-	hdev->version |= 0x8000;
-
-	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
-	if (ret) {
-		hid_err(hdev, "HW start failed\n");
-		goto err_wq;
-	}
-
-	ret = hid_hw_open(hdev);
-	if (ret) {
-		hid_err(hdev, "cannot start hardware I/O\n");
-		goto err_stop;
-	}
-
-	hid_device_io_start(hdev);
-
-	/* Initialize the controller */
-	mutex_lock(&ctlr->output_mutex);
-	/* if handshake command fails, assume ble pro controller */
-	if ((jc_type_is_procon(ctlr) || jc_type_is_chrggrip(ctlr)) &&
-	    !joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ)) {
-		hid_dbg(hdev, "detected USB controller\n");
-		/* set baudrate for improved latency */
-		ret = joycon_send_usb(ctlr, JC_USB_CMD_BAUDRATE_3M, HZ);
-		if (ret) {
-			hid_err(hdev, "Failed to set baudrate; ret=%d\n", ret);
-			goto err_mutex;
-		}
-		/* handshake */
-		ret = joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ);
-		if (ret) {
-			hid_err(hdev, "Failed handshake; ret=%d\n", ret);
-			goto err_mutex;
-		}
-		/*
-		 * Set no timeout (to keep controller in USB mode).
-		 * This doesn't send a response, so ignore the timeout.
-		 */
-		joycon_send_usb(ctlr, JC_USB_CMD_NO_TIMEOUT, HZ/10);
-	} else if (jc_type_is_chrggrip(ctlr)) {
-		hid_err(hdev, "Failed charging grip handshake\n");
-		ret = -ETIMEDOUT;
-		goto err_mutex;
-	}
-
-	/* get controller calibration data, and parse it */
-	ret = joycon_request_calibration(ctlr);
-	if (ret) {
-		/*
-		 * We can function with default calibration, but it may be
-		 * inaccurate. Provide a warning, and continue on.
-		 */
-		hid_warn(hdev, "Analog stick positions may be inaccurate\n");
-	}
-
-	/* get IMU calibration data, and parse it */
-	ret = joycon_request_imu_calibration(ctlr);
-	if (ret) {
-		/*
-		 * We can function with default calibration, but it may be
-		 * inaccurate. Provide a warning, and continue on.
-		 */
-		hid_warn(hdev, "Unable to read IMU calibration data\n");
-	}
-
-	/* Set the reporting mode to 0x30, which is the full report mode */
-	ret = joycon_set_report_mode(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to set report mode; ret=%d\n", ret);
-		goto err_mutex;
-	}
-
-	/* Enable rumble */
-	ret = joycon_enable_rumble(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to enable rumble; ret=%d\n", ret);
-		goto err_mutex;
-	}
-
-	/* Enable the IMU */
-	ret = joycon_enable_imu(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to enable the IMU; ret=%d\n", ret);
-		goto err_mutex;
-	}
-
-	ret = joycon_read_info(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to retrieve controller info; ret=%d\n",
-			ret);
-		goto err_mutex;
-	}
-
-	mutex_unlock(&ctlr->output_mutex);
-
-	/* Initialize the leds */
-	ret = joycon_leds_create(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to create leds; ret=%d\n", ret);
-		goto err_close;
-	}
-
-	/* Initialize the battery power supply */
-	ret = joycon_power_supply_create(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to create power_supply; ret=%d\n", ret);
-		goto err_close;
-	}
-
-	ret = joycon_input_create(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to create input device; ret=%d\n", ret);
-		goto err_close;
-	}
-
-	ctlr->ctlr_state = JOYCON_CTLR_STATE_READ;
-
-	hid_dbg(hdev, "probe - success\n");
-	return 0;
+#ifdef CONFIG_HID_NINTENDO_SWITCH
+	ret = switch_hid_probe(hdev, id);
+#endif
 
-err_mutex:
-	mutex_unlock(&ctlr->output_mutex);
-err_close:
-	hid_hw_close(hdev);
-err_stop:
-	hid_hw_stop(hdev);
-err_wq:
-	destroy_workqueue(ctlr->rumble_queue);
-err:
-	hid_err(hdev, "probe - fail = %d\n", ret);
 	return ret;
 }
 
 static void nintendo_hid_remove(struct hid_device *hdev)
 {
-	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
-	unsigned long flags;
+#ifdef CONFIG_HID_NINTENDO_SWITCH
+	enum nintendo_driver *driver = hid_get_drvdata(hdev);
 
-	hid_dbg(hdev, "remove\n");
-
-	/* Prevent further attempts at sending subcommands. */
-	spin_lock_irqsave(&ctlr->lock, flags);
-	ctlr->ctlr_state = JOYCON_CTLR_STATE_REMOVED;
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	destroy_workqueue(ctlr->rumble_queue);
-
-	hid_hw_close(hdev);
-	hid_hw_stop(hdev);
+	if (*driver == NINTENDO_SWITCH)
+		switch_hid_remove(hdev);
+#endif
 }
 
 static const struct hid_device_id nintendo_hid_devices[] = {
+#ifdef CONFIG_HID_NINTENDO_SWITCH
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
 			 USB_DEVICE_ID_NINTENDO_PROCON) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
@@ -2300,6 +59,8 @@ static const struct hid_device_id nintendo_hid_devices[] = {
 			 USB_DEVICE_ID_NINTENDO_JOYCONL) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
 			 USB_DEVICE_ID_NINTENDO_JOYCONR) },
+#endif
+
 	{ }
 };
 MODULE_DEVICE_TABLE(hid, nintendo_hid_devices);
@@ -2314,6 +75,6 @@ static struct hid_driver nintendo_hid_driver = {
 module_hid_driver(nintendo_hid_driver);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Daniel J. Ogorchock <djogorchock@gmail.com>");
-MODULE_DESCRIPTION("Driver for Nintendo Switch Controllers");
+MODULE_AUTHOR("Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>");
+MODULE_DESCRIPTION("Driver for Nintendo controllers");
 
diff --git a/drivers/hid/hid-nintendo.h b/drivers/hid/hid-nintendo.h
new file mode 100644
index 000000000000..56bb7e800cc2
--- /dev/null
+++ b/drivers/hid/hid-nintendo.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/kernel.h>
+#include <linux/hid.h>
+
+/* Every HID drvdata supported by this driver MUST start with this
+ * enum, so that dispatch can work properly. */
+enum nintendo_driver {
+	NINTENDO_SWITCH,
+};
+
+int switch_hid_event(struct hid_device *hdev,
+		     struct hid_report *report, u8 *raw_data, int size);
+int switch_hid_probe(struct hid_device *hdev,
+		     const struct hid_device_id *id);
+void switch_hid_remove(struct hid_device *hdev);
-- 
2.33.1


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

* [PATCH v4 2/5] HID: nintendo: drc: add support for the Wii U gamepad
  2021-10-19 11:04   ` [PATCH v4 0/5] HID: nintendo: Add support " Emmanuel Gil Peyrot
  2021-10-19 11:04     ` [PATCH v4 1/5] HID: nintendo: split switch support into its own file Emmanuel Gil Peyrot
@ 2021-10-19 11:04     ` Emmanuel Gil Peyrot
  2021-11-05 20:55       ` kernel test robot
  2021-10-19 11:04     ` [PATCH v4 3/5] HID: nintendo: drc: implement touch reports Emmanuel Gil Peyrot
                       ` (4 subsequent siblings)
  6 siblings, 1 reply; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-19 11:04 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Ash Logan, linux-input, Jonathan Neuschäfer,
	Barnabás Pőcze, Benjamin Tissoires, linux-kernel,
	Daniel J . Ogorchock, Emmanuel Gil Peyrot

From: Ash Logan <ash@heyquark.com>

This driver is for the DRC (gamepad) of the Wii U when wirelessly
connected to the DRH of the console, an internal chip exposing it as a
USB device.

This first patch exposes the buttons and sticks of this device, so that
it can act as a plain game controller.

The report format has been described by the libdrc folks at:
https://libdrc.org/docs/re/sc-input.html

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/Kconfig             |   7 +
 drivers/hid/Makefile            |   1 +
 drivers/hid/hid-ids.h           |   1 +
 drivers/hid/hid-nintendo-wiiu.c | 267 ++++++++++++++++++++++++++++++++
 drivers/hid/hid-nintendo.c      |  17 +-
 drivers/hid/hid-nintendo.h      |   6 +
 drivers/hid/hid-quirks.c        |   3 +
 7 files changed, 300 insertions(+), 2 deletions(-)
 create mode 100644 drivers/hid/hid-nintendo-wiiu.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 79beccefd431..9e75593e19cd 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -749,6 +749,13 @@ config NINTENDO_FF
 	controller. For the pro controller, both rumble motors can be controlled
 	individually.
 
+config HID_NINTENDO_WIIU
+	tristate "Nintendo Wii U gamepad (DRC) over internal DRH"
+	depends on HID_NINTENDO
+	help
+	  Support for the Wii U gamepad, when connected with the Wii U’s
+	  internal DRH chip.
+
 config HID_NINTENDO_SWITCH
 	tristate "Nintendo Wii U gamepad (DRC) over internal DRH"
 	depends on HID_NINTENDO
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index a0a9ee182ef2..f9851a7acea9 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_HID_MICROSOFT)	+= hid-microsoft.o
 obj-$(CONFIG_HID_MONTEREY)	+= hid-monterey.o
 obj-$(CONFIG_HID_MULTITOUCH)	+= hid-multitouch.o
 obj-$(CONFIG_HID_NINTENDO)	+= hid-nintendo.o
+obj-$(CONFIG_HID_NINTENDO_WIIU)	+= hid-nintendo-wiiu.o
 obj-$(CONFIG_HID_NINTENDO_SWITCH)	+= hid-nintendo-switch.o
 obj-$(CONFIG_HID_NTI)			+= hid-nti.o
 obj-$(CONFIG_HID_NTRIG)		+= hid-ntrig.o
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index dc8f2f402464..30ce4287c48b 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -917,6 +917,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_WIIU_DRH	0x0341
 #define USB_DEVICE_ID_NINTENDO_JOYCONL	0x2006
 #define USB_DEVICE_ID_NINTENDO_JOYCONR	0x2007
 #define USB_DEVICE_ID_NINTENDO_PROCON	0x2009
diff --git a/drivers/hid/hid-nintendo-wiiu.c b/drivers/hid/hid-nintendo-wiiu.c
new file mode 100644
index 000000000000..0b9df62587ed
--- /dev/null
+++ b/drivers/hid/hid-nintendo-wiiu.c
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID driver for Nintendo Wii U gamepad (DRC), connected via console-internal DRH
+ *
+ * Copyright (C) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+ * Copyright (C) 2019 Ash Logan <ash@heyquark.com>
+ * Copyright (C) 2013 Mema Hacking
+ *
+ * Based on the excellent work at http://libdrc.org/docs/re/sc-input.html and
+ * https://bitbucket.org/memahaxx/libdrc/src/master/src/input-receiver.cpp .
+ * libdrc code is licensed under BSD 2-Clause.
+ * Driver based on hid-udraw-ps3.c.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include "hid-ids.h"
+#include "hid-nintendo.h"
+
+#define DEVICE_NAME	"Nintendo Wii U gamepad (DRC)"
+
+/* Button and stick constants */
+#define VOLUME_MIN	0
+#define VOLUME_MAX	255
+#define NUM_STICK_AXES	4
+#define STICK_MIN	900
+#define STICK_MAX	3200
+
+#define BUTTON_SYNC	BIT(0)
+#define BUTTON_HOME	BIT(1)
+#define BUTTON_MINUS	BIT(2)
+#define BUTTON_PLUS	BIT(3)
+#define BUTTON_R	BIT(4)
+#define BUTTON_L	BIT(5)
+#define BUTTON_ZR	BIT(6)
+#define BUTTON_ZL	BIT(7)
+#define BUTTON_DOWN	BIT(8)
+#define BUTTON_UP	BIT(9)
+#define BUTTON_RIGHT	BIT(10)
+#define BUTTON_LEFT	BIT(11)
+#define BUTTON_Y	BIT(12)
+#define BUTTON_X	BIT(13)
+#define BUTTON_B	BIT(14)
+#define BUTTON_A	BIT(15)
+
+#define BUTTON_TV	BIT(21)
+#define BUTTON_R3	BIT(22)
+#define BUTTON_L3	BIT(23)
+
+#define BUTTON_POWER	BIT(25)
+
+/*
+ * The device is setup with multiple input devices:
+ * - A joypad with the buttons and sticks.
+ */
+
+struct drc {
+	enum nintendo_driver driver;
+	struct hid_device *hdev;
+	struct input_dev *joy_input_dev;
+};
+
+/*
+ * The format of this report has been reversed by the libdrc project, the
+ * documentation can be found here:
+ * https://libdrc.org/docs/re/sc-input.html
+ *
+ * We receive this report from USB, but it is actually formed on the DRC, the
+ * DRH only retransmits it over USB.
+ */
+int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
+		   u8 *data, int len)
+{
+	struct drc *drc = hid_get_drvdata(hdev);
+	int i;
+	u32 buttons;
+
+	if (len != 128)
+		return -EINVAL;
+
+	buttons = (data[4] << 24) | (data[80] << 16) | (data[2] << 8) | data[3];
+	/* joypad */
+	input_report_key(drc->joy_input_dev, BTN_DPAD_RIGHT, buttons & BUTTON_RIGHT);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_DOWN, buttons & BUTTON_DOWN);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_LEFT, buttons & BUTTON_LEFT);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_UP, buttons & BUTTON_UP);
+
+	input_report_key(drc->joy_input_dev, BTN_EAST, buttons & BUTTON_A);
+	input_report_key(drc->joy_input_dev, BTN_SOUTH, buttons & BUTTON_B);
+	input_report_key(drc->joy_input_dev, BTN_NORTH, buttons & BUTTON_X);
+	input_report_key(drc->joy_input_dev, BTN_WEST, buttons & BUTTON_Y);
+
+	input_report_key(drc->joy_input_dev, BTN_TL, buttons & BUTTON_L);
+	input_report_key(drc->joy_input_dev, BTN_TL2, buttons & BUTTON_ZL);
+	input_report_key(drc->joy_input_dev, BTN_TR, buttons & BUTTON_R);
+	input_report_key(drc->joy_input_dev, BTN_TR2, buttons & BUTTON_ZR);
+
+	input_report_key(drc->joy_input_dev, BTN_Z, buttons & BUTTON_TV);
+	input_report_key(drc->joy_input_dev, BTN_THUMBL, buttons & BUTTON_L3);
+	input_report_key(drc->joy_input_dev, BTN_THUMBR, buttons & BUTTON_R3);
+
+	input_report_key(drc->joy_input_dev, BTN_SELECT, buttons & BUTTON_MINUS);
+	input_report_key(drc->joy_input_dev, BTN_START, buttons & BUTTON_PLUS);
+	input_report_key(drc->joy_input_dev, BTN_MODE, buttons & BUTTON_HOME);
+
+	input_report_key(drc->joy_input_dev, BTN_DEAD, buttons & BUTTON_POWER);
+
+	for (i = 0; i < NUM_STICK_AXES; i++) {
+		s16 val = (data[7 + 2*i] << 8) | data[6 + 2*i];
+
+		val = clamp(val, (s16)STICK_MIN, (s16)STICK_MAX);
+
+		switch (i) {
+		case 0:
+			input_report_abs(drc->joy_input_dev, ABS_X, val);
+			break;
+		case 1:
+			input_report_abs(drc->joy_input_dev, ABS_Y, val);
+			break;
+		case 2:
+			input_report_abs(drc->joy_input_dev, ABS_RX, val);
+			break;
+		case 3:
+			input_report_abs(drc->joy_input_dev, ABS_RY, val);
+			break;
+		default:
+			break;
+		}
+	}
+
+	input_report_abs(drc->joy_input_dev, ABS_VOLUME, data[14]);
+	input_sync(drc->joy_input_dev);
+
+	/* let hidraw and hiddev handle the report */
+	return 0;
+}
+
+static int drc_open(struct input_dev *dev)
+{
+	struct drc *drc = input_get_drvdata(dev);
+
+	return hid_hw_open(drc->hdev);
+}
+
+static void drc_close(struct input_dev *dev)
+{
+	struct drc *drc = input_get_drvdata(dev);
+
+	hid_hw_close(drc->hdev);
+}
+
+static struct input_dev *allocate_and_setup(struct hid_device *hdev,
+					    const char *name)
+{
+	struct input_dev *input_dev;
+
+	input_dev = devm_input_allocate_device(&hdev->dev);
+	if (!input_dev)
+		return NULL;
+
+	input_dev->name = name;
+	input_dev->phys = hdev->phys;
+	input_dev->dev.parent = &hdev->dev;
+	input_dev->open = drc_open;
+	input_dev->close = drc_close;
+	input_dev->uniq = hdev->uniq;
+	input_dev->id.bustype = hdev->bus;
+	input_dev->id.vendor  = hdev->vendor;
+	input_dev->id.product = hdev->product;
+	input_dev->id.version = hdev->version;
+	input_set_drvdata(input_dev, hid_get_drvdata(hdev));
+
+	return input_dev;
+}
+
+static bool drc_setup_joypad(struct drc *drc,
+			     struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " buttons and sticks");
+	if (!input_dev)
+		return false;
+
+	drc->joy_input_dev = input_dev;
+
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_RIGHT);
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_DOWN);
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_LEFT);
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_UP);
+	input_set_capability(input_dev, EV_KEY, BTN_EAST);
+	input_set_capability(input_dev, EV_KEY, BTN_SOUTH);
+	input_set_capability(input_dev, EV_KEY, BTN_NORTH);
+	input_set_capability(input_dev, EV_KEY, BTN_WEST);
+	input_set_capability(input_dev, EV_KEY, BTN_TL);
+	input_set_capability(input_dev, EV_KEY, BTN_TL2);
+	input_set_capability(input_dev, EV_KEY, BTN_TR);
+	input_set_capability(input_dev, EV_KEY, BTN_TR2);
+	input_set_capability(input_dev, EV_KEY, BTN_THUMBL);
+	input_set_capability(input_dev, EV_KEY, BTN_THUMBR);
+	input_set_capability(input_dev, EV_KEY, BTN_SELECT);
+	input_set_capability(input_dev, EV_KEY, BTN_START);
+	input_set_capability(input_dev, EV_KEY, BTN_MODE);
+
+	/*
+	 * These two buttons are actually TV Control and Power.
+	 *
+	 * TV Control draws a line at the bottom of the DRC’s screen saying to
+	 * go into System Settings (on the original proprietary OS), while
+	 * Power will shutdown the DRC when held for four seconds, but those
+	 * two are still normal buttons otherwise.
+	 */
+	input_set_capability(input_dev, EV_KEY, BTN_Z);
+	input_set_capability(input_dev, EV_KEY, BTN_DEAD);
+
+	input_set_abs_params(input_dev, ABS_X, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RX, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RY, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_VOLUME, VOLUME_MIN, VOLUME_MAX, 0, 0);
+
+	return true;
+}
+
+int wiiu_hid_probe(struct hid_device *hdev,
+		   const struct hid_device_id *id)
+{
+	struct drc *drc;
+	int ret;
+
+	drc = devm_kzalloc(&hdev->dev, sizeof(struct drc), GFP_KERNEL);
+	if (!drc)
+		return -ENOMEM;
+
+	drc->driver = NINTENDO_WIIU;
+	drc->hdev = hdev;
+
+	hid_set_drvdata(hdev, drc);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "parse failed\n");
+		return ret;
+	}
+
+	if (!drc_setup_joypad(drc, hdev)) {
+		hid_err(hdev, "could not allocate interface\n");
+		return -ENOMEM;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER);
+	if (ret) {
+		hid_err(hdev, "hw start failed\n");
+		return ret;
+	}
+
+	ret = input_register_device(drc->joy_input_dev);
+	if (ret) {
+		hid_err(hdev, "failed to register interface\n");
+		return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index 9d67c7b2ac75..83397e387310 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -18,6 +18,10 @@ static int nintendo_hid_event(struct hid_device *hdev,
 {
 	enum nintendo_driver *driver = hid_get_drvdata(hdev);
 
+#ifdef CONFIG_HID_NINTENDO_WIIU
+	if (*driver == NINTENDO_WIIU)
+		return wiiu_hid_event(hdev, report, raw_data, size);
+#endif
 #ifdef CONFIG_HID_NINTENDO_SWITCH
 	if (*driver == NINTENDO_SWITCH)
 		return switch_hid_event(hdev, report, raw_data, size);
@@ -30,10 +34,14 @@ static int nintendo_hid_probe(struct hid_device *hdev,
 {
 	int ret;
 
+#ifdef CONFIG_HID_NINTENDO_WIIU
+	if (id->product == USB_DEVICE_ID_NINTENDO_WIIU_DRH)
+		ret = wiiu_hid_probe(hdev, id);
+#endif
 #ifdef CONFIG_HID_NINTENDO_SWITCH
-	ret = switch_hid_probe(hdev, id);
+	if (id->product != USB_DEVICE_ID_NINTENDO_WIIU_DRH)
+		ret = switch_hid_probe(hdev, id);
 #endif
-
 	return ret;
 }
 
@@ -48,6 +56,11 @@ static void nintendo_hid_remove(struct hid_device *hdev)
 }
 
 static const struct hid_device_id nintendo_hid_devices[] = {
+#ifdef CONFIG_HID_NINTENDO_WIIU
+	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
+			 USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
+#endif
+
 #ifdef CONFIG_HID_NINTENDO_SWITCH
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
 			 USB_DEVICE_ID_NINTENDO_PROCON) },
diff --git a/drivers/hid/hid-nintendo.h b/drivers/hid/hid-nintendo.h
index 56bb7e800cc2..eba300ee039e 100644
--- a/drivers/hid/hid-nintendo.h
+++ b/drivers/hid/hid-nintendo.h
@@ -6,9 +6,15 @@
 /* Every HID drvdata supported by this driver MUST start with this
  * enum, so that dispatch can work properly. */
 enum nintendo_driver {
+	NINTENDO_WIIU,
 	NINTENDO_SWITCH,
 };
 
+int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
+		   u8 *data, int len);
+int wiiu_hid_probe(struct hid_device *hdev,
+		   const struct hid_device_id *id);
+
 int switch_hid_event(struct hid_device *hdev,
 		     struct hid_report *report, u8 *raw_data, int size);
 int switch_hid_probe(struct hid_device *hdev,
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 2e104682c22b..6b13edfa195d 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -517,6 +517,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
 #endif
+#if IS_ENABLED(CONFIG_HID_WIIU_DRC)
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
+#endif
 #if IS_ENABLED(CONFIG_HID_NTI)
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NTI, USB_DEVICE_ID_USB_SUN) },
 #endif
-- 
2.33.1


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

* [PATCH v4 3/5] HID: nintendo: drc: implement touch reports
  2021-10-19 11:04   ` [PATCH v4 0/5] HID: nintendo: Add support " Emmanuel Gil Peyrot
  2021-10-19 11:04     ` [PATCH v4 1/5] HID: nintendo: split switch support into its own file Emmanuel Gil Peyrot
  2021-10-19 11:04     ` [PATCH v4 2/5] HID: nintendo: drc: add support for the Wii U gamepad Emmanuel Gil Peyrot
@ 2021-10-19 11:04     ` Emmanuel Gil Peyrot
  2021-10-19 11:04     ` [PATCH v4 4/5] HID: nintendo: drc: add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
                       ` (3 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-19 11:04 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock

There is an inaccessible border on each side, 100 units on the left and
right sides, and 200 units at the top and bottom.  The Y axis is
inverted too, these are the two main quirks of this touch panel.

I’ve been testing with weston-simple-touch mostly, but it also with the
rest of Weston and it aligns perfectly without the need of any
additional calibration.

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-nintendo-wiiu.c | 86 +++++++++++++++++++++++++++++++--
 1 file changed, 81 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/hid-nintendo-wiiu.c b/drivers/hid/hid-nintendo-wiiu.c
index 0b9df62587ed..144316d324cb 100644
--- a/drivers/hid/hid-nintendo-wiiu.c
+++ b/drivers/hid/hid-nintendo-wiiu.c
@@ -52,15 +52,29 @@
 
 #define BUTTON_POWER	BIT(25)
 
+/* Touch constants */
+/* Resolution in pixels */
+#define RES_X		854
+#define RES_Y		480
+/* Display/touch size in mm */
+#define WIDTH		138
+#define HEIGHT		79
+#define NUM_TOUCH_POINTS 10
+#define MAX_TOUCH_RES	(1 << 12)
+#define TOUCH_BORDER_X	100
+#define TOUCH_BORDER_Y	200
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
+ * - The touch area which works as a touchscreen.
  */
 
 struct drc {
 	enum nintendo_driver driver;
 	struct hid_device *hdev;
 	struct input_dev *joy_input_dev;
+	struct input_dev *touch_input_dev;
 };
 
 /*
@@ -75,7 +89,7 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 		   u8 *data, int len)
 {
 	struct drc *drc = hid_get_drvdata(hdev);
-	int i;
+	int i, x, y, pressure, base;
 	u32 buttons;
 
 	if (len != 128)
@@ -134,6 +148,42 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 	input_report_abs(drc->joy_input_dev, ABS_VOLUME, data[14]);
 	input_sync(drc->joy_input_dev);
 
+	/* touch */
+	/*
+	 * Average touch points for improved accuracy.  Sadly these are always
+	 * reported extremely close from each other…  Even when the user
+	 * pressed two (or more) different points, all ten values will be
+	 * approximately in the middle of the pressure points.
+	 */
+	x = y = 0;
+	for (i = 0; i < NUM_TOUCH_POINTS; i++) {
+		base = 36 + 4 * i;
+
+		x += ((data[base + 1] & 0xF) << 8) | data[base];
+		y += ((data[base + 3] & 0xF) << 8) | data[base + 2];
+	}
+	x /= NUM_TOUCH_POINTS;
+	y /= NUM_TOUCH_POINTS;
+
+	/* Pressure reporting isn’t properly understood, so we don’t report it yet. */
+	pressure = 0;
+	pressure |= ((data[37] >> 4) & 7) << 0;
+	pressure |= ((data[39] >> 4) & 7) << 3;
+	pressure |= ((data[41] >> 4) & 7) << 6;
+	pressure |= ((data[43] >> 4) & 7) << 9;
+
+	if (pressure != 0) {
+		input_report_key(drc->touch_input_dev, BTN_TOUCH, 1);
+		input_report_key(drc->touch_input_dev, BTN_TOOL_FINGER, 1);
+
+		input_report_abs(drc->touch_input_dev, ABS_X, x);
+		input_report_abs(drc->touch_input_dev, ABS_Y, MAX_TOUCH_RES - y);
+	} else {
+		input_report_key(drc->touch_input_dev, BTN_TOUCH, 0);
+		input_report_key(drc->touch_input_dev, BTN_TOOL_FINGER, 0);
+	}
+	input_sync(drc->touch_input_dev);
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -225,6 +275,30 @@ static bool drc_setup_joypad(struct drc *drc,
 	return true;
 }
 
+static bool drc_setup_touch(struct drc *drc,
+			    struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " touchscreen");
+	if (!input_dev)
+		return false;
+
+	drc->touch_input_dev = input_dev;
+
+	set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+	input_set_abs_params(input_dev, ABS_X, TOUCH_BORDER_X, MAX_TOUCH_RES - TOUCH_BORDER_X, 20, 0);
+	input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH);
+	input_set_abs_params(input_dev, ABS_Y, TOUCH_BORDER_Y, MAX_TOUCH_RES - TOUCH_BORDER_Y, 20, 0);
+	input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT);
+
+	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+	input_set_capability(input_dev, EV_KEY, BTN_TOOL_FINGER);
+
+	return true;
+}
+
 int wiiu_hid_probe(struct hid_device *hdev,
 		   const struct hid_device_id *id)
 {
@@ -246,8 +320,9 @@ int wiiu_hid_probe(struct hid_device *hdev,
 		return ret;
 	}
 
-	if (!drc_setup_joypad(drc, hdev)) {
-		hid_err(hdev, "could not allocate interface\n");
+	if (!drc_setup_joypad(drc, hdev) ||
+	    !drc_setup_touch(drc, hdev)) {
+		hid_err(hdev, "could not allocate interfaces\n");
 		return -ENOMEM;
 	}
 
@@ -257,9 +332,10 @@ int wiiu_hid_probe(struct hid_device *hdev,
 		return ret;
 	}
 
-	ret = input_register_device(drc->joy_input_dev);
+	ret = input_register_device(drc->joy_input_dev) ||
+	      input_register_device(drc->touch_input_dev);
 	if (ret) {
-		hid_err(hdev, "failed to register interface\n");
+		hid_err(hdev, "failed to register interfaces\n");
 		return ret;
 	}
 
-- 
2.33.1


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

* [PATCH v4 4/5] HID: nintendo: drc: add accelerometer, gyroscope and magnetometer readings
  2021-10-19 11:04   ` [PATCH v4 0/5] HID: nintendo: Add support " Emmanuel Gil Peyrot
                       ` (2 preceding siblings ...)
  2021-10-19 11:04     ` [PATCH v4 3/5] HID: nintendo: drc: implement touch reports Emmanuel Gil Peyrot
@ 2021-10-19 11:04     ` Emmanuel Gil Peyrot
  2021-10-19 11:04     ` [PATCH v4 5/5] HID: nintendo: drc: add battery reporting Emmanuel Gil Peyrot
                       ` (2 subsequent siblings)
  6 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-19 11:04 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock

These are mostly untested so far, because I have no idea which userland
to test against, but evtest at least seems to give sensible values.

The magnetometer doesn’t have dedicated INPUT_PROP_ACCELEROMETER
buttons, so I used three clearly invalid absolute values, in the hope
that someone will fix that eventually.  Another solution might be to go
for the iio subsystem instead, but then it wouldn’t be tied to the HID
any longer and I would feel uneasy about that.  Especially because
multiple such gamepads could be connected to a single computer.

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-nintendo-wiiu.c | 77 +++++++++++++++++++++++++++++++--
 1 file changed, 74 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/hid-nintendo-wiiu.c b/drivers/hid/hid-nintendo-wiiu.c
index 144316d324cb..813abb104275 100644
--- a/drivers/hid/hid-nintendo-wiiu.c
+++ b/drivers/hid/hid-nintendo-wiiu.c
@@ -64,10 +64,19 @@
 #define TOUCH_BORDER_X	100
 #define TOUCH_BORDER_Y	200
 
+/* Accelerometer, gyroscope and magnetometer constants */
+#define ACCEL_MIN	-(1 << 15)
+#define ACCEL_MAX	((1 << 15) - 1)
+#define GYRO_MIN	-(1 << 23)
+#define GYRO_MAX	((1 << 23) - 1)
+#define MAGNET_MIN	-(1 << 15)
+#define MAGNET_MAX	((1 << 15) - 1)
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
  * - The touch area which works as a touchscreen.
+ * - An accelerometer + gyroscope + magnetometer device.
  */
 
 struct drc {
@@ -75,6 +84,7 @@ struct drc {
 	struct hid_device *hdev;
 	struct input_dev *joy_input_dev;
 	struct input_dev *touch_input_dev;
+	struct input_dev *accel_input_dev;
 };
 
 /*
@@ -89,7 +99,7 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 		   u8 *data, int len)
 {
 	struct drc *drc = hid_get_drvdata(hdev);
-	int i, x, y, pressure, base;
+	int i, x, y, z, pressure, base;
 	u32 buttons;
 
 	if (len != 128)
@@ -184,6 +194,31 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 	}
 	input_sync(drc->touch_input_dev);
 
+	/* accelerometer */
+	x = (data[16] << 8) | data[15];
+	y = (data[18] << 8) | data[17];
+	z = (data[20] << 8) | data[19];
+	input_report_abs(drc->accel_input_dev, ABS_X, (int16_t)x);
+	input_report_abs(drc->accel_input_dev, ABS_Y, (int16_t)y);
+	input_report_abs(drc->accel_input_dev, ABS_Z, (int16_t)z);
+
+	/* gyroscope */
+	x = (data[23] << 24) | (data[22] << 16) | (data[21] << 8);
+	y = (data[26] << 24) | (data[25] << 16) | (data[24] << 8);
+	z = (data[29] << 24) | (data[28] << 16) | (data[27] << 8);
+	input_report_abs(drc->accel_input_dev, ABS_RX, x >> 8);
+	input_report_abs(drc->accel_input_dev, ABS_RY, y >> 8);
+	input_report_abs(drc->accel_input_dev, ABS_RZ, z >> 8);
+
+	/* magnetometer */
+	x = (data[31] << 8) | data[30];
+	y = (data[33] << 8) | data[32];
+	z = (data[35] << 8) | data[34];
+	input_report_abs(drc->accel_input_dev, ABS_THROTTLE, (int16_t)x);
+	input_report_abs(drc->accel_input_dev, ABS_RUDDER, (int16_t)y);
+	input_report_abs(drc->accel_input_dev, ABS_WHEEL, (int16_t)z);
+	input_sync(drc->accel_input_dev);
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -299,6 +334,40 @@ static bool drc_setup_touch(struct drc *drc,
 	return true;
 }
 
+static bool drc_setup_accel(struct drc *drc,
+			    struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " accelerometer, gyroscope and magnetometer");
+	if (!input_dev)
+		return false;
+
+	drc->accel_input_dev = input_dev;
+
+	set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit);
+
+	/* 1G accel is reported as about -7900 */
+	input_set_abs_params(input_dev, ABS_X, ACCEL_MIN, ACCEL_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, ACCEL_MIN, ACCEL_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Z, ACCEL_MIN, ACCEL_MAX, 0, 0);
+
+	/* gyroscope */
+	input_set_abs_params(input_dev, ABS_RX, GYRO_MIN, GYRO_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RY, GYRO_MIN, GYRO_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RZ, GYRO_MIN, GYRO_MAX, 0, 0);
+
+	/* magnetometer */
+	/* TODO: Figure out which ABS_* would make more sense to expose, or
+	 * maybe go for the iio subsystem?
+	 */
+	input_set_abs_params(input_dev, ABS_THROTTLE, MAGNET_MIN, MAGNET_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RUDDER, MAGNET_MIN, MAGNET_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_WHEEL, MAGNET_MIN, MAGNET_MAX, 0, 0);
+
+	return true;
+}
+
 int wiiu_hid_probe(struct hid_device *hdev,
 		   const struct hid_device_id *id)
 {
@@ -321,7 +390,8 @@ int wiiu_hid_probe(struct hid_device *hdev,
 	}
 
 	if (!drc_setup_joypad(drc, hdev) ||
-	    !drc_setup_touch(drc, hdev)) {
+	    !drc_setup_touch(drc, hdev) ||
+	    !drc_setup_accel(drc, hdev)) {
 		hid_err(hdev, "could not allocate interfaces\n");
 		return -ENOMEM;
 	}
@@ -333,7 +403,8 @@ int wiiu_hid_probe(struct hid_device *hdev,
 	}
 
 	ret = input_register_device(drc->joy_input_dev) ||
-	      input_register_device(drc->touch_input_dev);
+	      input_register_device(drc->touch_input_dev) ||
+	      input_register_device(drc->accel_input_dev);
 	if (ret) {
 		hid_err(hdev, "failed to register interfaces\n");
 		return ret;
-- 
2.33.1


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

* [PATCH v4 5/5] HID: nintendo: drc: add battery reporting
  2021-10-19 11:04   ` [PATCH v4 0/5] HID: nintendo: Add support " Emmanuel Gil Peyrot
                       ` (3 preceding siblings ...)
  2021-10-19 11:04     ` [PATCH v4 4/5] HID: nintendo: drc: add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
@ 2021-10-19 11:04     ` Emmanuel Gil Peyrot
  2021-10-27  8:10     ` [PATCH v4 0/5] HID: nintendo: Add support for the Wii U gamepad Jiri Kosina
  2021-10-27 10:10     ` [PATCH v5 " Emmanuel Gil Peyrot
  6 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-19 11:04 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock

On my DRC the values only go between 142 (battery LED blinking red
before shutdown) and 178 (charge LED stopping), it seems to be the same
on other units according to other testers.  This might be the raw
voltage value as reported by an ADC, so adding a linear interpolation
between two common battery voltage values.

A spinlock is used to avoid the battery level and status from being
reported unsynchronised.

Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-nintendo-wiiu.c | 136 ++++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/drivers/hid/hid-nintendo-wiiu.c b/drivers/hid/hid-nintendo-wiiu.c
index 813abb104275..b18fa403eb42 100644
--- a/drivers/hid/hid-nintendo-wiiu.c
+++ b/drivers/hid/hid-nintendo-wiiu.c
@@ -17,6 +17,11 @@
 #include <linux/input.h>
 #include <linux/minmax.h>
 #include <linux/module.h>
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+#include <linux/fixp-arith.h>
+#include <linux/power_supply.h>
+#include <linux/spinlock.h>
+#endif
 #include "hid-ids.h"
 #include "hid-nintendo.h"
 
@@ -72,6 +77,13 @@
 #define MAGNET_MIN	-(1 << 15)
 #define MAGNET_MAX	((1 << 15) - 1)
 
+/* ADC constants for the battery */
+#define BATTERY_CHARGING_BIT	BIT(6)
+#define BATTERY_MIN	142
+#define BATTERY_MAX	178
+#define VOLTAGE_MIN	3270000
+#define VOLTAGE_MAX	4100000
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
@@ -85,6 +97,14 @@ struct drc {
 	struct input_dev *joy_input_dev;
 	struct input_dev *touch_input_dev;
 	struct input_dev *accel_input_dev;
+
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	struct power_supply *battery;
+	struct power_supply_desc battery_desc;
+	spinlock_t battery_lock;
+	u8 battery_energy;
+	int battery_status;
+#endif
 };
 
 /*
@@ -101,6 +121,9 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 	struct drc *drc = hid_get_drvdata(hdev);
 	int i, x, y, z, pressure, base;
 	u32 buttons;
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	unsigned long flags;
+#endif
 
 	if (len != 128)
 		return -EINVAL;
@@ -219,6 +242,19 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 	input_report_abs(drc->accel_input_dev, ABS_WHEEL, (int16_t)z);
 	input_sync(drc->accel_input_dev);
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	/* battery */
+	spin_lock_irqsave(&drc->battery_lock, flags);
+	drc->battery_energy = data[5];
+	if (drc->battery_energy == BATTERY_MAX)
+		drc->battery_status = POWER_SUPPLY_STATUS_FULL;
+	else if (data[4] & BATTERY_CHARGING_BIT)
+		drc->battery_status = POWER_SUPPLY_STATUS_CHARGING;
+	else
+		drc->battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+	spin_unlock_irqrestore(&drc->battery_lock, flags);
+#endif
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -368,6 +404,98 @@ static bool drc_setup_accel(struct drc *drc,
 	return true;
 }
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+static enum power_supply_property drc_battery_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_SCOPE,
+};
+
+static int drc_battery_get_property(struct power_supply *psy,
+				    enum power_supply_property psp,
+				    union power_supply_propval *val)
+{
+	struct drc *drc = power_supply_get_drvdata(psy);
+	unsigned long flags;
+	int ret = 0;
+	u8 battery_energy;
+	int battery_status;
+
+	spin_lock_irqsave(&drc->battery_lock, flags);
+	battery_energy = drc->battery_energy;
+	battery_status = drc->battery_status;
+	spin_unlock_irqrestore(&drc->battery_lock, flags);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = battery_status;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		val->intval = VOLTAGE_MAX;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		val->intval = VOLTAGE_MIN;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = fixp_linear_interpolate(BATTERY_MIN, VOLTAGE_MIN,
+						      BATTERY_MAX, VOLTAGE_MAX,
+						      battery_energy);
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = fixp_linear_interpolate(BATTERY_MIN, 0,
+						      BATTERY_MAX, 100,
+						      battery_energy);
+		break;
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int drc_setup_battery(struct drc *drc,
+			     struct hid_device *hdev)
+{
+	struct power_supply_config psy_cfg = { .drv_data = drc, };
+	static atomic_t drc_num = ATOMIC_INIT(0);
+	int ret;
+
+	spin_lock_init(&drc->battery_lock);
+
+	drc->battery_desc.properties = drc_battery_props;
+	drc->battery_desc.num_properties = ARRAY_SIZE(drc_battery_props);
+	drc->battery_desc.get_property = drc_battery_get_property;
+	drc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+	drc->battery_desc.use_for_apm = 0;
+
+	drc->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
+						"wiiu-drc-%i-battery", atomic_fetch_inc(&drc_num));
+	if (!drc->battery_desc.name)
+		return -ENOMEM;
+
+	drc->battery = devm_power_supply_register(&hdev->dev, &drc->battery_desc, &psy_cfg);
+	if (IS_ERR(drc->battery)) {
+		ret = PTR_ERR(drc->battery);
+		hid_err(hdev, "Unable to register battery device\n");
+		return ret;
+	}
+
+	power_supply_powers(drc->battery, &hdev->dev);
+
+	return 0;
+}
+#endif
+
 int wiiu_hid_probe(struct hid_device *hdev,
 		   const struct hid_device_id *id)
 {
@@ -396,6 +524,14 @@ int wiiu_hid_probe(struct hid_device *hdev,
 		return -ENOMEM;
 	}
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	ret = drc_setup_battery(drc, hdev);
+	if (ret) {
+		hid_err(hdev, "could not allocate battery interface\n");
+		return ret;
+	}
+#endif
+
 	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER);
 	if (ret) {
 		hid_err(hdev, "hw start failed\n");
-- 
2.33.1


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

* Re: [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad
  2021-10-19  9:27       ` Emmanuel Gil Peyrot
  2021-10-19  9:30         ` Jiri Kosina
@ 2021-10-19 23:59         ` François-Xavier Carton
  2021-10-20  6:24           ` Emmanuel Gil Peyrot
  1 sibling, 1 reply; 41+ messages in thread
From: François-Xavier Carton @ 2021-10-19 23:59 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot
  Cc: Jiri Kosina, linux-input, Ash Logan, Jonathan Neuschäfer,
	Barnabás Pőcze, Benjamin Tissoires, linux-kernel,
	Daniel J. Ogorchock

Hi,

On Tue, Oct 19, 2021 at 11:27:37AM +0200, Emmanuel Gil Peyrot wrote:
> I don’t see any existing driver named that way in mainline, would it be
> acceptable to simply rename the current patches to hid-nintendo?  What
> should be done about the existing hid-wiimote driver then, should it
> also be merged alongside?
> 
> Another driver I’d like to submit eventually is the GameCube Controller
> Adapter for Wii U, which does exactly what its name says, but being an
> external USB adapter it also works on any USB computer; would it make
> sense to develop it alongside the current driver, just because it is
> sold by the same company?
> 

FYI, I've submitted a GC adapter driver previously [1]. I've been using
it since then; the patch still applies to recent kernels. I'd be happy
to work towards having this driver mainlined, if there is interest for
it.

[1] https://patchwork.kernel.org/project/linux-input/list/?series=282859&state=*

Best,
François-Xavier

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

* Re: [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad
  2021-10-19 23:59         ` François-Xavier Carton
@ 2021-10-20  6:24           ` Emmanuel Gil Peyrot
  0 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-20  6:24 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot, Jiri Kosina, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J. Ogorchock

[-- Attachment #1: Type: text/plain, Size: 1275 bytes --]

On Wed, Oct 20, 2021 at 01:59:43AM +0200, François-Xavier Carton wrote:
> Hi,
> 
> On Tue, Oct 19, 2021 at 11:27:37AM +0200, Emmanuel Gil Peyrot wrote:
> > I don’t see any existing driver named that way in mainline, would it be
> > acceptable to simply rename the current patches to hid-nintendo?  What
> > should be done about the existing hid-wiimote driver then, should it
> > also be merged alongside?
> > 
> > Another driver I’d like to submit eventually is the GameCube Controller
> > Adapter for Wii U, which does exactly what its name says, but being an
> > external USB adapter it also works on any USB computer; would it make
> > sense to develop it alongside the current driver, just because it is
> > sold by the same company?
> > 
> 
> FYI, I've submitted a GC adapter driver previously [1]. I've been using
> it since then; the patch still applies to recent kernels. I'd be happy
> to work towards having this driver mainlined, if there is interest for
> it.

I am definitely interested!  I have been using a user-space driver for
the time being, but that’s not a good solution.

> 
> [1] https://patchwork.kernel.org/project/linux-input/list/?series=282859&state=*
> 
> Best,
> François-Xavier

-- 
Emmanuel Gil Peyrot

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH v4 1/5] HID: nintendo: split switch support into its own file
  2021-10-19 11:04     ` [PATCH v4 1/5] HID: nintendo: split switch support into its own file Emmanuel Gil Peyrot
@ 2021-10-22  8:32       ` kernel test robot
  0 siblings, 0 replies; 41+ messages in thread
From: kernel test robot @ 2021-10-22  8:32 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot, Jiri Kosina
  Cc: kbuild-all, Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock

[-- Attachment #1: Type: text/plain, Size: 2389 bytes --]

Hi Emmanuel,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on hid/for-next]
[cannot apply to jikos-hid/for-next jikos-trivial/for-next v5.15-rc6 next-20211021]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Emmanuel-Gil-Peyrot/HID-nintendo-split-switch-support-into-its-own-file/20211019-200531
base:   https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git for-next
config: arm-randconfig-c002-20211019 (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/ef6cb9d1fa01a825280bca49c84b644b8d08bfa6
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Emmanuel-Gil-Peyrot/HID-nintendo-split-switch-support-into-its-own-file/20211019-200531
        git checkout ef6cb9d1fa01a825280bca49c84b644b8d08bfa6
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=arm 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/hid/hid-nintendo.c: In function 'nintendo_hid_event':
   drivers/hid/hid-nintendo.c:19:31: warning: unused variable 'driver' [-Wunused-variable]
      19 |         enum nintendo_driver *driver = hid_get_drvdata(hdev);
         |                               ^~~~~~
   drivers/hid/hid-nintendo.c: In function 'nintendo_hid_probe':
>> drivers/hid/hid-nintendo.c:37:16: warning: 'ret' is used uninitialized [-Wuninitialized]
      37 |         return ret;
         |                ^~~


vim +/ret +37 drivers/hid/hid-nintendo.c

c680902a57658c Daniel J. Ogorchock 2021-09-11  36  
c680902a57658c Daniel J. Ogorchock 2021-09-11 @37  	return ret;
c680902a57658c Daniel J. Ogorchock 2021-09-11  38  }
c680902a57658c Daniel J. Ogorchock 2021-09-11  39  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 39019 bytes --]

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

* Re: [PATCH v4 0/5] HID: nintendo: Add support for the Wii U gamepad
  2021-10-19 11:04   ` [PATCH v4 0/5] HID: nintendo: Add support " Emmanuel Gil Peyrot
                       ` (4 preceding siblings ...)
  2021-10-19 11:04     ` [PATCH v4 5/5] HID: nintendo: drc: add battery reporting Emmanuel Gil Peyrot
@ 2021-10-27  8:10     ` Jiri Kosina
  2021-10-27 10:10     ` [PATCH v5 " Emmanuel Gil Peyrot
  6 siblings, 0 replies; 41+ messages in thread
From: Jiri Kosina @ 2021-10-27  8:10 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot
  Cc: linux-input, Ash Logan, Jonathan Neuschäfer,
	Barnabás Pőcze, Benjamin Tissoires, linux-kernel,
	Daniel J . Ogorchock

On Tue, 19 Oct 2021, Emmanuel Gil Peyrot wrote:

> This driver is for the DRC (wireless gamepad) when plugged to the DRH of
> the Wii U, a chip exposing it as a USB device.
> 
> I tried to use this driver on master over usbip on my laptop, but usbip
> disconnects the device right after the driver created the
> /dev/input/event* files, so instead I have only tested this driver on
> the 4.19 branch of the linux-wiiu[1] downstream.
> 
> Other than that, pretty much all of the HID parts of the gamepad work,
> it’s only missing microphone, camera and NFC input now but those are
> mostly standard (read require quirks) and pertain to other subsystems,
> so I felt like this can be upstreamed already.

Now that proper hid.git#for-5.16/nintendo branch exists, could you please 
fix up the issues reported by the kernel build bot and resubmit?

Thanks,

-- 
Jiri Kosina
SUSE Labs


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

* [PATCH v5 0/5] HID: nintendo: Add support for the Wii U gamepad
  2021-10-19 11:04   ` [PATCH v4 0/5] HID: nintendo: Add support " Emmanuel Gil Peyrot
                       ` (5 preceding siblings ...)
  2021-10-27  8:10     ` [PATCH v4 0/5] HID: nintendo: Add support for the Wii U gamepad Jiri Kosina
@ 2021-10-27 10:10     ` Emmanuel Gil Peyrot
  2021-10-27 10:10       ` [PATCH v5 1/5] HID: nintendo: split switch support into its own file Emmanuel Gil Peyrot
                         ` (4 more replies)
  6 siblings, 5 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-27 10:10 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock

This driver is for the DRC (wireless gamepad) when plugged to the DRH of
the Wii U, a chip exposing it as a USB device.

I tried to use this driver on master over usbip on my laptop, but usbip
disconnects the device right after the driver created the
/dev/input/event* files, so instead I have only tested this driver on
the 4.19 branch of the linux-wiiu[1] downstream.

Other than that, pretty much all of the HID parts of the gamepad work,
it’s only missing microphone, camera and NFC input now but those are
mostly standard (read require quirks) and pertain to other subsystems,
so I felt like this can be upstreamed already.

[1] https://gitlab.com/linux-wiiu/linux-wiiu

Changes since v1:
- Rename interfaces to be less redundant.
- Add comments for potentially unclear things.
- Reword some commits to include more needed information.
- Include all needed includes.
- Use helpful helper functions instead of (badly) reimplementing them
  myself.
- Always return the correct type for each function, to avoid some
  bool/int confusion, or returning 0 to mean error.
- Use myself as the module author, even though Ash did most of the
  initial work, I’m the one who will be maintaining this module from now
  on.
- Use input_set_capability() instead of set_bit(…, keybit) to also set
  BIT(EV_KEY) on evbit[0].
- Call hid_hw_start() before input_register_device() but after the setup
  functions, so that hid_hw_open() is never called before it.
- Add missing spin_lock_init() for the battery lock.
- Use a static atomic for the drc_num, and remove the comment about
  using the interface number.
- Interpret battery report as the voltage coming from an ADC, instead of
  the completely wrong ENERGY_NOW it was before.

So basically addressing Jonathan’s and Barnabás’ comments. :)

Changes since v2:
- Guard against the possibility of CONFIG_HID_BATTERY_STRENGTH not
  having been selected.
- Include forgotten linux/fixp-arith.h header.
- Fix a warning in clamp() due to comparing a s16 with a #define.

Changes since v3:
- Rebased on top of hid.git#for-5.16/nintendo.
- Merged into hid-nintendo.
- Make hid-nintendo mostly a stub, with sub-drivers implementing the
  actual controllers.
- Introduced CONFIG_HID_NINTENDO_WIIU to enable support for this device
  now.

Changes since v4:
- Fix warnings when no sub-driver has been selected (thanks Intel CI!).
- Default Switch sub-driver to y and Wii U sub-driver to n, as the
  latter is mostly only useful on an actual console and that won’t be
  the common case when using hid-nintendo.

Ash Logan (1):
  HID: nintendo: drc: add support for the Wii U gamepad

Emmanuel Gil Peyrot (4):
  HID: nintendo: split switch support into its own file
  HID: nintendo: drc: implement touch reports
  HID: nintendo: drc: add accelerometer, gyroscope and magnetometer
    readings
  HID: nintendo: drc: add battery reporting

 drivers/hid/Kconfig                           |   33 +-
 drivers/hid/Makefile                          |    2 +
 drivers/hid/hid-ids.h                         |    1 +
 .../{hid-nintendo.c => hid-nintendo-switch.c} |   43 +-
 drivers/hid/hid-nintendo-wiiu.c               |  550 ++++
 drivers/hid/hid-nintendo.c                    | 2304 +----------------
 drivers/hid/hid-nintendo.h                    |   22 +
 drivers/hid/hid-quirks.c                      |    3 +
 8 files changed, 650 insertions(+), 2308 deletions(-)
 copy drivers/hid/{hid-nintendo.c => hid-nintendo-switch.c} (98%)
 create mode 100644 drivers/hid/hid-nintendo-wiiu.c
 create mode 100644 drivers/hid/hid-nintendo.h

-- 
2.33.1


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

* [PATCH v5 1/5] HID: nintendo: split switch support into its own file
  2021-10-27 10:10     ` [PATCH v5 " Emmanuel Gil Peyrot
@ 2021-10-27 10:10       ` Emmanuel Gil Peyrot
  2021-10-27 10:10       ` [PATCH v5 2/5] HID: nintendo: drc: add support for the Wii U gamepad Emmanuel Gil Peyrot
                         ` (3 subsequent siblings)
  4 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-27 10:10 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock

This allows for other controllers to be supported, which do not require
the same set of features.

Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/Kconfig                           |   25 +-
 drivers/hid/Makefile                          |    1 +
 .../{hid-nintendo.c => hid-nintendo-switch.c} |   43 +-
 drivers/hid/hid-nintendo.c                    | 2287 +----------------
 drivers/hid/hid-nintendo.h                    |   16 +
 5 files changed, 65 insertions(+), 2307 deletions(-)
 copy drivers/hid/{hid-nintendo.c => hid-nintendo-switch.c} (98%)
 create mode 100644 drivers/hid/hid-nintendo.h

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 62d4a6d6ba13..17ed06f5a23c 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -731,18 +731,13 @@ config HID_MULTITOUCH
 	  module will be called hid-multitouch.
 
 config HID_NINTENDO
-	tristate "Nintendo Joy-Con and Pro Controller support"
+	tristate "Nintendo controllers support"
 	depends on HID
-	depends on NEW_LEDS
-	depends on LEDS_CLASS
-	select POWER_SUPPLY
 	help
-	Adds support for the Nintendo Switch Joy-Cons and Pro Controller.
-	All controllers support bluetooth, and the Pro Controller also supports
-	its USB mode.
+	  Adds support for various Nintendo controllers.
 
-	To compile this driver as a module, choose M here: the
-	module will be called hid-nintendo.
+	  To compile this driver as a module, choose M here: the
+	  module will be called hid-nintendo.
 
 config NINTENDO_FF
 	bool "Nintendo Switch controller force feedback support"
@@ -754,6 +749,18 @@ config NINTENDO_FF
 	controller. For the pro controller, both rumble motors can be controlled
 	individually.
 
+config HID_NINTENDO_SWITCH
+	tristate "Nintendo Wii U gamepad (DRC) over internal DRH"
+	default y
+	depends on HID_NINTENDO
+	depends on NEW_LEDS
+	depends on LEDS_CLASS
+	select POWER_SUPPLY
+	help
+	  Adds support for the Nintendo Switch Joy-Cons and Pro Controller.
+	  All controllers support bluetooth, and the Pro Controller also supports
+	  its USB mode.
+
 config HID_NTI
 	tristate "NTI keyboard adapters"
 	help
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 7a71371e3adf..a0a9ee182ef2 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_HID_MICROSOFT)	+= hid-microsoft.o
 obj-$(CONFIG_HID_MONTEREY)	+= hid-monterey.o
 obj-$(CONFIG_HID_MULTITOUCH)	+= hid-multitouch.o
 obj-$(CONFIG_HID_NINTENDO)	+= hid-nintendo.o
+obj-$(CONFIG_HID_NINTENDO_SWITCH)	+= hid-nintendo-switch.o
 obj-$(CONFIG_HID_NTI)			+= hid-nti.o
 obj-$(CONFIG_HID_NTRIG)		+= hid-ntrig.o
 obj-$(CONFIG_HID_ORTEK)		+= hid-ortek.o
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo-switch.c
similarity index 98%
copy from drivers/hid/hid-nintendo.c
copy to drivers/hid/hid-nintendo-switch.c
index c4270499fc6f..b742ed45b601 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo-switch.c
@@ -23,6 +23,7 @@
  */
 
 #include "hid-ids.h"
+#include "hid-nintendo.h"
 #include <asm/unaligned.h>
 #include <linux/delay.h>
 #include <linux/device.h>
@@ -31,7 +32,6 @@
 #include <linux/input.h>
 #include <linux/jiffies.h>
 #include <linux/leds.h>
-#include <linux/module.h>
 #include <linux/power_supply.h>
 #include <linux/spinlock.h>
 
@@ -412,6 +412,7 @@ static const char * const joycon_player_led_names[] = {
 
 /* Each physical controller is associated with a joycon_ctlr struct */
 struct joycon_ctlr {
+	enum nintendo_driver driver;
 	struct hid_device *hdev;
 	struct input_dev *input;
 	struct led_classdev leds[JC_NUM_LEDS]; /* player leds */
@@ -2088,8 +2089,8 @@ static int joycon_ctlr_handle_event(struct joycon_ctlr *ctlr, u8 *data,
 	return ret;
 }
 
-static int nintendo_hid_event(struct hid_device *hdev,
-			      struct hid_report *report, u8 *raw_data, int size)
+int switch_hid_event(struct hid_device *hdev,
+		     struct hid_report *report, u8 *raw_data, int size)
 {
 	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
 
@@ -2099,8 +2100,8 @@ static int nintendo_hid_event(struct hid_device *hdev,
 	return joycon_ctlr_handle_event(ctlr, raw_data, size);
 }
 
-static int nintendo_hid_probe(struct hid_device *hdev,
-			    const struct hid_device_id *id)
+int switch_hid_probe(struct hid_device *hdev,
+		     const struct hid_device_id *id)
 {
 	int ret;
 	struct joycon_ctlr *ctlr;
@@ -2113,6 +2114,7 @@ static int nintendo_hid_probe(struct hid_device *hdev,
 		goto err;
 	}
 
+	ctlr->driver = NINTENDO_SWITCH;
 	ctlr->hdev = hdev;
 	ctlr->ctlr_state = JOYCON_CTLR_STATE_INIT;
 	ctlr->rumble_queue_head = JC_RUMBLE_QUEUE_SIZE - 1;
@@ -2271,7 +2273,7 @@ static int nintendo_hid_probe(struct hid_device *hdev,
 	return ret;
 }
 
-static void nintendo_hid_remove(struct hid_device *hdev)
+void switch_hid_remove(struct hid_device *hdev)
 {
 	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
 	unsigned long flags;
@@ -2288,32 +2290,3 @@ static void nintendo_hid_remove(struct hid_device *hdev)
 	hid_hw_close(hdev);
 	hid_hw_stop(hdev);
 }
-
-static const struct hid_device_id nintendo_hid_devices[] = {
-	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
-			 USB_DEVICE_ID_NINTENDO_PROCON) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
-			 USB_DEVICE_ID_NINTENDO_PROCON) },
-	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
-			 USB_DEVICE_ID_NINTENDO_CHRGGRIP) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
-			 USB_DEVICE_ID_NINTENDO_JOYCONL) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
-			 USB_DEVICE_ID_NINTENDO_JOYCONR) },
-	{ }
-};
-MODULE_DEVICE_TABLE(hid, nintendo_hid_devices);
-
-static struct hid_driver nintendo_hid_driver = {
-	.name		= "nintendo",
-	.id_table	= nintendo_hid_devices,
-	.probe		= nintendo_hid_probe,
-	.remove		= nintendo_hid_remove,
-	.raw_event	= nintendo_hid_event,
-};
-module_hid_driver(nintendo_hid_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Daniel J. Ogorchock <djogorchock@gmail.com>");
-MODULE_DESCRIPTION("Driver for Nintendo Switch Controllers");
-
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index c4270499fc6f..f16d8d94b1da 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -1,2295 +1,54 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * HID driver for Nintendo Switch Joy-Cons and Pro Controllers
- *
- * Copyright (c) 2019-2021 Daniel J. Ogorchock <djogorchock@gmail.com>
- *
- * The following resources/projects were referenced for this driver:
- *   https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
- *   https://gitlab.com/pjranki/joycon-linux-kernel (Peter Rankin)
- *   https://github.com/FrotBot/SwitchProConLinuxUSB
- *   https://github.com/MTCKC/ProconXInput
- *   https://github.com/Davidobot/BetterJoyForCemu
- *   hid-wiimote kernel hid driver
- *   hid-logitech-hidpp driver
- *   hid-sony driver
- *
- * This driver supports the Nintendo Switch Joy-Cons and Pro Controllers. The
- * Pro Controllers can either be used over USB or Bluetooth.
- *
- * The driver will retrieve the factory calibration info from the controllers,
- * so little to no user calibration should be required.
+ * HID driver for Nintendo controllers
  *
+ * Copyright (c) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  */
 
 #include "hid-ids.h"
-#include <asm/unaligned.h>
-#include <linux/delay.h>
+#include "hid-nintendo.h"
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/hid.h>
 #include <linux/input.h>
-#include <linux/jiffies.h>
-#include <linux/leds.h>
 #include <linux/module.h>
-#include <linux/power_supply.h>
-#include <linux/spinlock.h>
-
-/*
- * Reference the url below for the following HID report defines:
- * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
- */
-
-/* Output Reports */
-static const u8 JC_OUTPUT_RUMBLE_AND_SUBCMD	= 0x01;
-static const u8 JC_OUTPUT_FW_UPDATE_PKT		= 0x03;
-static const u8 JC_OUTPUT_RUMBLE_ONLY		= 0x10;
-static const u8 JC_OUTPUT_MCU_DATA		= 0x11;
-static const u8 JC_OUTPUT_USB_CMD		= 0x80;
-
-/* Subcommand IDs */
-static const u8 JC_SUBCMD_STATE			/*= 0x00*/;
-static const u8 JC_SUBCMD_MANUAL_BT_PAIRING	= 0x01;
-static const u8 JC_SUBCMD_REQ_DEV_INFO		= 0x02;
-static const u8 JC_SUBCMD_SET_REPORT_MODE	= 0x03;
-static const u8 JC_SUBCMD_TRIGGERS_ELAPSED	= 0x04;
-static const u8 JC_SUBCMD_GET_PAGE_LIST_STATE	= 0x05;
-static const u8 JC_SUBCMD_SET_HCI_STATE		= 0x06;
-static const u8 JC_SUBCMD_RESET_PAIRING_INFO	= 0x07;
-static const u8 JC_SUBCMD_LOW_POWER_MODE	= 0x08;
-static const u8 JC_SUBCMD_SPI_FLASH_READ	= 0x10;
-static const u8 JC_SUBCMD_SPI_FLASH_WRITE	= 0x11;
-static const u8 JC_SUBCMD_RESET_MCU		= 0x20;
-static const u8 JC_SUBCMD_SET_MCU_CONFIG	= 0x21;
-static const u8 JC_SUBCMD_SET_MCU_STATE		= 0x22;
-static const u8 JC_SUBCMD_SET_PLAYER_LIGHTS	= 0x30;
-static const u8 JC_SUBCMD_GET_PLAYER_LIGHTS	= 0x31;
-static const u8 JC_SUBCMD_SET_HOME_LIGHT	= 0x38;
-static const u8 JC_SUBCMD_ENABLE_IMU		= 0x40;
-static const u8 JC_SUBCMD_SET_IMU_SENSITIVITY	= 0x41;
-static const u8 JC_SUBCMD_WRITE_IMU_REG		= 0x42;
-static const u8 JC_SUBCMD_READ_IMU_REG		= 0x43;
-static const u8 JC_SUBCMD_ENABLE_VIBRATION	= 0x48;
-static const u8 JC_SUBCMD_GET_REGULATED_VOLTAGE	= 0x50;
-
-/* Input Reports */
-static const u8 JC_INPUT_BUTTON_EVENT		= 0x3F;
-static const u8 JC_INPUT_SUBCMD_REPLY		= 0x21;
-static const u8 JC_INPUT_IMU_DATA		= 0x30;
-static const u8 JC_INPUT_MCU_DATA		= 0x31;
-static const u8 JC_INPUT_USB_RESPONSE		= 0x81;
-
-/* Feature Reports */
-static const u8 JC_FEATURE_LAST_SUBCMD		= 0x02;
-static const u8 JC_FEATURE_OTA_FW_UPGRADE	= 0x70;
-static const u8 JC_FEATURE_SETUP_MEM_READ	= 0x71;
-static const u8 JC_FEATURE_MEM_READ		= 0x72;
-static const u8 JC_FEATURE_ERASE_MEM_SECTOR	= 0x73;
-static const u8 JC_FEATURE_MEM_WRITE		= 0x74;
-static const u8 JC_FEATURE_LAUNCH		= 0x75;
-
-/* USB Commands */
-static const u8 JC_USB_CMD_CONN_STATUS		= 0x01;
-static const u8 JC_USB_CMD_HANDSHAKE		= 0x02;
-static const u8 JC_USB_CMD_BAUDRATE_3M		= 0x03;
-static const u8 JC_USB_CMD_NO_TIMEOUT		= 0x04;
-static const u8 JC_USB_CMD_EN_TIMEOUT		= 0x05;
-static const u8 JC_USB_RESET			= 0x06;
-static const u8 JC_USB_PRE_HANDSHAKE		= 0x91;
-static const u8 JC_USB_SEND_UART		= 0x92;
-
-/* Magic value denoting presence of user calibration */
-static const u16 JC_CAL_USR_MAGIC_0		= 0xB2;
-static const u16 JC_CAL_USR_MAGIC_1		= 0xA1;
-static const u8 JC_CAL_USR_MAGIC_SIZE		= 2;
-
-/* SPI storage addresses of user calibration data */
-static const u16 JC_CAL_USR_LEFT_MAGIC_ADDR	= 0x8010;
-static const u16 JC_CAL_USR_LEFT_DATA_ADDR	= 0x8012;
-static const u16 JC_CAL_USR_LEFT_DATA_END	= 0x801A;
-static const u16 JC_CAL_USR_RIGHT_MAGIC_ADDR	= 0x801B;
-static const u16 JC_CAL_USR_RIGHT_DATA_ADDR	= 0x801D;
-#define JC_CAL_STICK_DATA_SIZE \
-	(JC_CAL_USR_LEFT_DATA_END - JC_CAL_USR_LEFT_DATA_ADDR + 1)
-
-/* SPI storage addresses of factory calibration data */
-static const u16 JC_CAL_FCT_DATA_LEFT_ADDR	= 0x603d;
-static const u16 JC_CAL_FCT_DATA_RIGHT_ADDR	= 0x6046;
-
-/* SPI storage addresses of IMU factory calibration data */
-static const u16 JC_IMU_CAL_FCT_DATA_ADDR	= 0x6020;
-static const u16 JC_IMU_CAL_FCT_DATA_END	= 0x6037;
-#define JC_IMU_CAL_DATA_SIZE \
-	(JC_IMU_CAL_FCT_DATA_END - JC_IMU_CAL_FCT_DATA_ADDR + 1)
-/* SPI storage addresses of IMU user calibration data */
-static const u16 JC_IMU_CAL_USR_MAGIC_ADDR	= 0x8026;
-static const u16 JC_IMU_CAL_USR_DATA_ADDR	= 0x8028;
-
-/* The raw analog joystick values will be mapped in terms of this magnitude */
-static const u16 JC_MAX_STICK_MAG		= 32767;
-static const u16 JC_STICK_FUZZ			= 250;
-static const u16 JC_STICK_FLAT			= 500;
-
-/* Hat values for pro controller's d-pad */
-static const u16 JC_MAX_DPAD_MAG		= 1;
-static const u16 JC_DPAD_FUZZ			/*= 0*/;
-static const u16 JC_DPAD_FLAT			/*= 0*/;
-
-/* Under most circumstances IMU reports are pushed every 15ms; use as default */
-static const u16 JC_IMU_DFLT_AVG_DELTA_MS	= 15;
-/* How many samples to sum before calculating average IMU report delta */
-static const u16 JC_IMU_SAMPLES_PER_DELTA_AVG	= 300;
-/* Controls how many dropped IMU packets at once trigger a warning message */
-static const u16 JC_IMU_DROPPED_PKT_WARNING	= 3;
-
-/*
- * The controller's accelerometer has a sensor resolution of 16bits and is
- * configured with a range of +-8000 milliGs. Therefore, the resolution can be
- * calculated thus: (2^16-1)/(8000 * 2) = 4.096 digits per milliG
- * Resolution per G (rather than per millliG): 4.096 * 1000 = 4096 digits per G
- * Alternatively: 1/4096 = .0002441 Gs per digit
- */
-static const s32 JC_IMU_MAX_ACCEL_MAG		= 32767;
-static const u16 JC_IMU_ACCEL_RES_PER_G		= 4096;
-static const u16 JC_IMU_ACCEL_FUZZ		= 10;
-static const u16 JC_IMU_ACCEL_FLAT		/*= 0*/;
-
-/*
- * The controller's gyroscope has a sensor resolution of 16bits and is
- * configured with a range of +-2000 degrees/second.
- * Digits per dps: (2^16 -1)/(2000*2) = 16.38375
- * dps per digit: 16.38375E-1 = .0610
- *
- * STMicro recommends in the datasheet to add 15% to the dps/digit. This allows
- * the full sensitivity range to be saturated without clipping. This yields more
- * accurate results, so it's the technique this driver uses.
- * dps per digit (corrected): .0610 * 1.15 = .0702
- * digits per dps (corrected): .0702E-1 = 14.247
- *
- * Now, 14.247 truncating to 14 loses a lot of precision, so we rescale the
- * min/max range by 1000.
- */
-static const s32 JC_IMU_PREC_RANGE_SCALE	= 1000;
-/* Note: change mag and res_per_dps if prec_range_scale is ever altered */
-static const s32 JC_IMU_MAX_GYRO_MAG		= 32767000; /* (2^16-1)*1000 */
-static const u16 JC_IMU_GYRO_RES_PER_DPS	= 14247; /* (14.247*1000) */
-static const u16 JC_IMU_GYRO_FUZZ		= 10;
-static const u16 JC_IMU_GYRO_FLAT		/*= 0*/;
-
-/* frequency/amplitude tables for rumble */
-struct joycon_rumble_freq_data {
-	u16 high;
-	u8 low;
-	u16 freq; /* Hz*/
-};
-
-struct joycon_rumble_amp_data {
-	u8 high;
-	u16 low;
-	u16 amp;
-};
-
-/*
- * These tables are from
- * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
- */
-static const struct joycon_rumble_freq_data joycon_rumble_frequencies[] = {
-	/* high, low, freq */
-	{ 0x0000, 0x01,   41 }, { 0x0000, 0x02,   42 }, { 0x0000, 0x03,   43 },
-	{ 0x0000, 0x04,   44 }, { 0x0000, 0x05,   45 }, { 0x0000, 0x06,   46 },
-	{ 0x0000, 0x07,   47 }, { 0x0000, 0x08,   48 }, { 0x0000, 0x09,   49 },
-	{ 0x0000, 0x0A,   50 }, { 0x0000, 0x0B,   51 }, { 0x0000, 0x0C,   52 },
-	{ 0x0000, 0x0D,   53 }, { 0x0000, 0x0E,   54 }, { 0x0000, 0x0F,   55 },
-	{ 0x0000, 0x10,   57 }, { 0x0000, 0x11,   58 }, { 0x0000, 0x12,   59 },
-	{ 0x0000, 0x13,   60 }, { 0x0000, 0x14,   62 }, { 0x0000, 0x15,   63 },
-	{ 0x0000, 0x16,   64 }, { 0x0000, 0x17,   66 }, { 0x0000, 0x18,   67 },
-	{ 0x0000, 0x19,   69 }, { 0x0000, 0x1A,   70 }, { 0x0000, 0x1B,   72 },
-	{ 0x0000, 0x1C,   73 }, { 0x0000, 0x1D,   75 }, { 0x0000, 0x1e,   77 },
-	{ 0x0000, 0x1f,   78 }, { 0x0000, 0x20,   80 }, { 0x0400, 0x21,   82 },
-	{ 0x0800, 0x22,   84 }, { 0x0c00, 0x23,   85 }, { 0x1000, 0x24,   87 },
-	{ 0x1400, 0x25,   89 }, { 0x1800, 0x26,   91 }, { 0x1c00, 0x27,   93 },
-	{ 0x2000, 0x28,   95 }, { 0x2400, 0x29,   97 }, { 0x2800, 0x2a,   99 },
-	{ 0x2c00, 0x2b,  102 }, { 0x3000, 0x2c,  104 }, { 0x3400, 0x2d,  106 },
-	{ 0x3800, 0x2e,  108 }, { 0x3c00, 0x2f,  111 }, { 0x4000, 0x30,  113 },
-	{ 0x4400, 0x31,  116 }, { 0x4800, 0x32,  118 }, { 0x4c00, 0x33,  121 },
-	{ 0x5000, 0x34,  123 }, { 0x5400, 0x35,  126 }, { 0x5800, 0x36,  129 },
-	{ 0x5c00, 0x37,  132 }, { 0x6000, 0x38,  135 }, { 0x6400, 0x39,  137 },
-	{ 0x6800, 0x3a,  141 }, { 0x6c00, 0x3b,  144 }, { 0x7000, 0x3c,  147 },
-	{ 0x7400, 0x3d,  150 }, { 0x7800, 0x3e,  153 }, { 0x7c00, 0x3f,  157 },
-	{ 0x8000, 0x40,  160 }, { 0x8400, 0x41,  164 }, { 0x8800, 0x42,  167 },
-	{ 0x8c00, 0x43,  171 }, { 0x9000, 0x44,  174 }, { 0x9400, 0x45,  178 },
-	{ 0x9800, 0x46,  182 }, { 0x9c00, 0x47,  186 }, { 0xa000, 0x48,  190 },
-	{ 0xa400, 0x49,  194 }, { 0xa800, 0x4a,  199 }, { 0xac00, 0x4b,  203 },
-	{ 0xb000, 0x4c,  207 }, { 0xb400, 0x4d,  212 }, { 0xb800, 0x4e,  217 },
-	{ 0xbc00, 0x4f,  221 }, { 0xc000, 0x50,  226 }, { 0xc400, 0x51,  231 },
-	{ 0xc800, 0x52,  236 }, { 0xcc00, 0x53,  241 }, { 0xd000, 0x54,  247 },
-	{ 0xd400, 0x55,  252 }, { 0xd800, 0x56,  258 }, { 0xdc00, 0x57,  263 },
-	{ 0xe000, 0x58,  269 }, { 0xe400, 0x59,  275 }, { 0xe800, 0x5a,  281 },
-	{ 0xec00, 0x5b,  287 }, { 0xf000, 0x5c,  293 }, { 0xf400, 0x5d,  300 },
-	{ 0xf800, 0x5e,  306 }, { 0xfc00, 0x5f,  313 }, { 0x0001, 0x60,  320 },
-	{ 0x0401, 0x61,  327 }, { 0x0801, 0x62,  334 }, { 0x0c01, 0x63,  341 },
-	{ 0x1001, 0x64,  349 }, { 0x1401, 0x65,  357 }, { 0x1801, 0x66,  364 },
-	{ 0x1c01, 0x67,  372 }, { 0x2001, 0x68,  381 }, { 0x2401, 0x69,  389 },
-	{ 0x2801, 0x6a,  397 }, { 0x2c01, 0x6b,  406 }, { 0x3001, 0x6c,  415 },
-	{ 0x3401, 0x6d,  424 }, { 0x3801, 0x6e,  433 }, { 0x3c01, 0x6f,  443 },
-	{ 0x4001, 0x70,  453 }, { 0x4401, 0x71,  462 }, { 0x4801, 0x72,  473 },
-	{ 0x4c01, 0x73,  483 }, { 0x5001, 0x74,  494 }, { 0x5401, 0x75,  504 },
-	{ 0x5801, 0x76,  515 }, { 0x5c01, 0x77,  527 }, { 0x6001, 0x78,  538 },
-	{ 0x6401, 0x79,  550 }, { 0x6801, 0x7a,  562 }, { 0x6c01, 0x7b,  574 },
-	{ 0x7001, 0x7c,  587 }, { 0x7401, 0x7d,  600 }, { 0x7801, 0x7e,  613 },
-	{ 0x7c01, 0x7f,  626 }, { 0x8001, 0x00,  640 }, { 0x8401, 0x00,  654 },
-	{ 0x8801, 0x00,  668 }, { 0x8c01, 0x00,  683 }, { 0x9001, 0x00,  698 },
-	{ 0x9401, 0x00,  713 }, { 0x9801, 0x00,  729 }, { 0x9c01, 0x00,  745 },
-	{ 0xa001, 0x00,  761 }, { 0xa401, 0x00,  778 }, { 0xa801, 0x00,  795 },
-	{ 0xac01, 0x00,  812 }, { 0xb001, 0x00,  830 }, { 0xb401, 0x00,  848 },
-	{ 0xb801, 0x00,  867 }, { 0xbc01, 0x00,  886 }, { 0xc001, 0x00,  905 },
-	{ 0xc401, 0x00,  925 }, { 0xc801, 0x00,  945 }, { 0xcc01, 0x00,  966 },
-	{ 0xd001, 0x00,  987 }, { 0xd401, 0x00, 1009 }, { 0xd801, 0x00, 1031 },
-	{ 0xdc01, 0x00, 1053 }, { 0xe001, 0x00, 1076 }, { 0xe401, 0x00, 1100 },
-	{ 0xe801, 0x00, 1124 }, { 0xec01, 0x00, 1149 }, { 0xf001, 0x00, 1174 },
-	{ 0xf401, 0x00, 1199 }, { 0xf801, 0x00, 1226 }, { 0xfc01, 0x00, 1253 }
-};
-
-#define joycon_max_rumble_amp	(1003)
-static const struct joycon_rumble_amp_data joycon_rumble_amplitudes[] = {
-	/* high, low, amp */
-	{ 0x00, 0x0040,    0 },
-	{ 0x02, 0x8040,   10 }, { 0x04, 0x0041,   12 }, { 0x06, 0x8041,   14 },
-	{ 0x08, 0x0042,   17 }, { 0x0a, 0x8042,   20 }, { 0x0c, 0x0043,   24 },
-	{ 0x0e, 0x8043,   28 }, { 0x10, 0x0044,   33 }, { 0x12, 0x8044,   40 },
-	{ 0x14, 0x0045,   47 }, { 0x16, 0x8045,   56 }, { 0x18, 0x0046,   67 },
-	{ 0x1a, 0x8046,   80 }, { 0x1c, 0x0047,   95 }, { 0x1e, 0x8047,  112 },
-	{ 0x20, 0x0048,  117 }, { 0x22, 0x8048,  123 }, { 0x24, 0x0049,  128 },
-	{ 0x26, 0x8049,  134 }, { 0x28, 0x004a,  140 }, { 0x2a, 0x804a,  146 },
-	{ 0x2c, 0x004b,  152 }, { 0x2e, 0x804b,  159 }, { 0x30, 0x004c,  166 },
-	{ 0x32, 0x804c,  173 }, { 0x34, 0x004d,  181 }, { 0x36, 0x804d,  189 },
-	{ 0x38, 0x004e,  198 }, { 0x3a, 0x804e,  206 }, { 0x3c, 0x004f,  215 },
-	{ 0x3e, 0x804f,  225 }, { 0x40, 0x0050,  230 }, { 0x42, 0x8050,  235 },
-	{ 0x44, 0x0051,  240 }, { 0x46, 0x8051,  245 }, { 0x48, 0x0052,  251 },
-	{ 0x4a, 0x8052,  256 }, { 0x4c, 0x0053,  262 }, { 0x4e, 0x8053,  268 },
-	{ 0x50, 0x0054,  273 }, { 0x52, 0x8054,  279 }, { 0x54, 0x0055,  286 },
-	{ 0x56, 0x8055,  292 }, { 0x58, 0x0056,  298 }, { 0x5a, 0x8056,  305 },
-	{ 0x5c, 0x0057,  311 }, { 0x5e, 0x8057,  318 }, { 0x60, 0x0058,  325 },
-	{ 0x62, 0x8058,  332 }, { 0x64, 0x0059,  340 }, { 0x66, 0x8059,  347 },
-	{ 0x68, 0x005a,  355 }, { 0x6a, 0x805a,  362 }, { 0x6c, 0x005b,  370 },
-	{ 0x6e, 0x805b,  378 }, { 0x70, 0x005c,  387 }, { 0x72, 0x805c,  395 },
-	{ 0x74, 0x005d,  404 }, { 0x76, 0x805d,  413 }, { 0x78, 0x005e,  422 },
-	{ 0x7a, 0x805e,  431 }, { 0x7c, 0x005f,  440 }, { 0x7e, 0x805f,  450 },
-	{ 0x80, 0x0060,  460 }, { 0x82, 0x8060,  470 }, { 0x84, 0x0061,  480 },
-	{ 0x86, 0x8061,  491 }, { 0x88, 0x0062,  501 }, { 0x8a, 0x8062,  512 },
-	{ 0x8c, 0x0063,  524 }, { 0x8e, 0x8063,  535 }, { 0x90, 0x0064,  547 },
-	{ 0x92, 0x8064,  559 }, { 0x94, 0x0065,  571 }, { 0x96, 0x8065,  584 },
-	{ 0x98, 0x0066,  596 }, { 0x9a, 0x8066,  609 }, { 0x9c, 0x0067,  623 },
-	{ 0x9e, 0x8067,  636 }, { 0xa0, 0x0068,  650 }, { 0xa2, 0x8068,  665 },
-	{ 0xa4, 0x0069,  679 }, { 0xa6, 0x8069,  694 }, { 0xa8, 0x006a,  709 },
-	{ 0xaa, 0x806a,  725 }, { 0xac, 0x006b,  741 }, { 0xae, 0x806b,  757 },
-	{ 0xb0, 0x006c,  773 }, { 0xb2, 0x806c,  790 }, { 0xb4, 0x006d,  808 },
-	{ 0xb6, 0x806d,  825 }, { 0xb8, 0x006e,  843 }, { 0xba, 0x806e,  862 },
-	{ 0xbc, 0x006f,  881 }, { 0xbe, 0x806f,  900 }, { 0xc0, 0x0070,  920 },
-	{ 0xc2, 0x8070,  940 }, { 0xc4, 0x0071,  960 }, { 0xc6, 0x8071,  981 },
-	{ 0xc8, 0x0072, joycon_max_rumble_amp }
-};
-
-/* States for controller state machine */
-enum joycon_ctlr_state {
-	JOYCON_CTLR_STATE_INIT,
-	JOYCON_CTLR_STATE_READ,
-	JOYCON_CTLR_STATE_REMOVED,
-};
-
-/* Controller type received as part of device info */
-enum joycon_ctlr_type {
-	JOYCON_CTLR_TYPE_JCL = 0x01,
-	JOYCON_CTLR_TYPE_JCR = 0x02,
-	JOYCON_CTLR_TYPE_PRO = 0x03,
-};
-
-struct joycon_stick_cal {
-	s32 max;
-	s32 min;
-	s32 center;
-};
-
-struct joycon_imu_cal {
-	s16 offset[3];
-	s16 scale[3];
-};
-
-/*
- * All the controller's button values are stored in a u32.
- * They can be accessed with bitwise ANDs.
- */
-static const u32 JC_BTN_Y	= BIT(0);
-static const u32 JC_BTN_X	= BIT(1);
-static const u32 JC_BTN_B	= BIT(2);
-static const u32 JC_BTN_A	= BIT(3);
-static const u32 JC_BTN_SR_R	= BIT(4);
-static const u32 JC_BTN_SL_R	= BIT(5);
-static const u32 JC_BTN_R	= BIT(6);
-static const u32 JC_BTN_ZR	= BIT(7);
-static const u32 JC_BTN_MINUS	= BIT(8);
-static const u32 JC_BTN_PLUS	= BIT(9);
-static const u32 JC_BTN_RSTICK	= BIT(10);
-static const u32 JC_BTN_LSTICK	= BIT(11);
-static const u32 JC_BTN_HOME	= BIT(12);
-static const u32 JC_BTN_CAP	= BIT(13); /* capture button */
-static const u32 JC_BTN_DOWN	= BIT(16);
-static const u32 JC_BTN_UP	= BIT(17);
-static const u32 JC_BTN_RIGHT	= BIT(18);
-static const u32 JC_BTN_LEFT	= BIT(19);
-static const u32 JC_BTN_SR_L	= BIT(20);
-static const u32 JC_BTN_SL_L	= BIT(21);
-static const u32 JC_BTN_L	= BIT(22);
-static const u32 JC_BTN_ZL	= BIT(23);
-
-enum joycon_msg_type {
-	JOYCON_MSG_TYPE_NONE,
-	JOYCON_MSG_TYPE_USB,
-	JOYCON_MSG_TYPE_SUBCMD,
-};
-
-struct joycon_rumble_output {
-	u8 output_id;
-	u8 packet_num;
-	u8 rumble_data[8];
-} __packed;
-
-struct joycon_subcmd_request {
-	u8 output_id; /* must be 0x01 for subcommand, 0x10 for rumble only */
-	u8 packet_num; /* incremented every send */
-	u8 rumble_data[8];
-	u8 subcmd_id;
-	u8 data[]; /* length depends on the subcommand */
-} __packed;
-
-struct joycon_subcmd_reply {
-	u8 ack; /* MSB 1 for ACK, 0 for NACK */
-	u8 id; /* id of requested subcmd */
-	u8 data[]; /* will be at most 35 bytes */
-} __packed;
-
-struct joycon_imu_data {
-	s16 accel_x;
-	s16 accel_y;
-	s16 accel_z;
-	s16 gyro_x;
-	s16 gyro_y;
-	s16 gyro_z;
-} __packed;
-
-struct joycon_input_report {
-	u8 id;
-	u8 timer;
-	u8 bat_con; /* battery and connection info */
-	u8 button_status[3];
-	u8 left_stick[3];
-	u8 right_stick[3];
-	u8 vibrator_report;
-
-	union {
-		struct joycon_subcmd_reply subcmd_reply;
-		/* IMU input reports contain 3 samples */
-		u8 imu_raw_bytes[sizeof(struct joycon_imu_data) * 3];
-	};
-} __packed;
-
-#define JC_MAX_RESP_SIZE	(sizeof(struct joycon_input_report) + 35)
-#define JC_RUMBLE_DATA_SIZE	8
-#define JC_RUMBLE_QUEUE_SIZE	8
-
-static const u16 JC_RUMBLE_DFLT_LOW_FREQ = 160;
-static const u16 JC_RUMBLE_DFLT_HIGH_FREQ = 320;
-static const u16 JC_RUMBLE_PERIOD_MS = 50;
-static const unsigned short JC_RUMBLE_ZERO_AMP_PKT_CNT = 5;
-
-static const char * const joycon_player_led_names[] = {
-	LED_FUNCTION_PLAYER1,
-	LED_FUNCTION_PLAYER2,
-	LED_FUNCTION_PLAYER3,
-	LED_FUNCTION_PLAYER4,
-};
-#define JC_NUM_LEDS		ARRAY_SIZE(joycon_player_led_names)
-
-/* Each physical controller is associated with a joycon_ctlr struct */
-struct joycon_ctlr {
-	struct hid_device *hdev;
-	struct input_dev *input;
-	struct led_classdev leds[JC_NUM_LEDS]; /* player leds */
-	struct led_classdev home_led;
-	enum joycon_ctlr_state ctlr_state;
-	spinlock_t lock;
-	u8 mac_addr[6];
-	char *mac_addr_str;
-	enum joycon_ctlr_type ctlr_type;
-
-	/* The following members are used for synchronous sends/receives */
-	enum joycon_msg_type msg_type;
-	u8 subcmd_num;
-	struct mutex output_mutex;
-	u8 input_buf[JC_MAX_RESP_SIZE];
-	wait_queue_head_t wait;
-	bool received_resp;
-	u8 usb_ack_match;
-	u8 subcmd_ack_match;
-	bool received_input_report;
-	unsigned int last_subcmd_sent_msecs;
-
-	/* factory calibration data */
-	struct joycon_stick_cal left_stick_cal_x;
-	struct joycon_stick_cal left_stick_cal_y;
-	struct joycon_stick_cal right_stick_cal_x;
-	struct joycon_stick_cal right_stick_cal_y;
-
-	struct joycon_imu_cal accel_cal;
-	struct joycon_imu_cal gyro_cal;
-
-	/* prevents needlessly recalculating these divisors every sample */
-	s32 imu_cal_accel_divisor[3];
-	s32 imu_cal_gyro_divisor[3];
-
-	/* power supply data */
-	struct power_supply *battery;
-	struct power_supply_desc battery_desc;
-	u8 battery_capacity;
-	bool battery_charging;
-	bool host_powered;
-
-	/* rumble */
-	u8 rumble_data[JC_RUMBLE_QUEUE_SIZE][JC_RUMBLE_DATA_SIZE];
-	int rumble_queue_head;
-	int rumble_queue_tail;
-	struct workqueue_struct *rumble_queue;
-	struct work_struct rumble_worker;
-	unsigned int rumble_msecs;
-	u16 rumble_ll_freq;
-	u16 rumble_lh_freq;
-	u16 rumble_rl_freq;
-	u16 rumble_rh_freq;
-	unsigned short rumble_zero_countdown;
-
-	/* imu */
-	struct input_dev *imu_input;
-	bool imu_first_packet_received; /* helps in initiating timestamp */
-	unsigned int imu_timestamp_us; /* timestamp we report to userspace */
-	unsigned int imu_last_pkt_ms; /* used to calc imu report delta */
-	/* the following are used to track the average imu report time delta */
-	unsigned int imu_delta_samples_count;
-	unsigned int imu_delta_samples_sum;
-	unsigned int imu_avg_delta_ms;
-};
-
-/* Helper macros for checking controller type */
-#define jc_type_is_joycon(ctlr) \
-	(ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONL || \
-	 ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONR || \
-	 ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP)
-#define jc_type_is_procon(ctlr) \
-	(ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_PROCON)
-#define jc_type_is_chrggrip(ctlr) \
-	(ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP)
-
-/* Does this controller have inputs associated with left joycon? */
-#define jc_type_has_left(ctlr) \
-	(ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCL || \
-	 ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO)
-
-/* Does this controller have inputs associated with right joycon? */
-#define jc_type_has_right(ctlr) \
-	(ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCR || \
-	 ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO)
-
-static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len)
-{
-	u8 *buf;
-	int ret;
-
-	buf = kmemdup(data, len, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-	ret = hid_hw_output_report(hdev, buf, len);
-	kfree(buf);
-	if (ret < 0)
-		hid_dbg(hdev, "Failed to send output report ret=%d\n", ret);
-	return ret;
-}
-
-static void joycon_wait_for_input_report(struct joycon_ctlr *ctlr)
-{
-	int ret;
-
-	/*
-	 * If we are in the proper reporting mode, wait for an input
-	 * report prior to sending the subcommand. This improves
-	 * reliability considerably.
-	 */
-	if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) {
-		unsigned long flags;
-
-		spin_lock_irqsave(&ctlr->lock, flags);
-		ctlr->received_input_report = false;
-		spin_unlock_irqrestore(&ctlr->lock, flags);
-		ret = wait_event_timeout(ctlr->wait,
-					 ctlr->received_input_report,
-					 HZ / 4);
-		/* We will still proceed, even with a timeout here */
-		if (!ret)
-			hid_warn(ctlr->hdev,
-				 "timeout waiting for input report\n");
-	}
-}
-
-/*
- * Sending subcommands and/or rumble data at too high a rate can cause bluetooth
- * controller disconnections.
- */
-static void joycon_enforce_subcmd_rate(struct joycon_ctlr *ctlr)
-{
-	static const unsigned int max_subcmd_rate_ms = 25;
-	unsigned int current_ms = jiffies_to_msecs(jiffies);
-	unsigned int delta_ms = current_ms - ctlr->last_subcmd_sent_msecs;
-
-	while (delta_ms < max_subcmd_rate_ms &&
-	       ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) {
-		joycon_wait_for_input_report(ctlr);
-		current_ms = jiffies_to_msecs(jiffies);
-		delta_ms = current_ms - ctlr->last_subcmd_sent_msecs;
-	}
-	ctlr->last_subcmd_sent_msecs = current_ms;
-}
-
-static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len,
-				u32 timeout)
-{
-	int ret;
-	int tries = 2;
-
-	/*
-	 * The controller occasionally seems to drop subcommands. In testing,
-	 * doing one retry after a timeout appears to always work.
-	 */
-	while (tries--) {
-		joycon_enforce_subcmd_rate(ctlr);
-
-		ret = __joycon_hid_send(ctlr->hdev, data, len);
-		if (ret < 0) {
-			memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE);
-			return ret;
-		}
-
-		ret = wait_event_timeout(ctlr->wait, ctlr->received_resp,
-					 timeout);
-		if (!ret) {
-			hid_dbg(ctlr->hdev,
-				"synchronous send/receive timed out\n");
-			if (tries) {
-				hid_dbg(ctlr->hdev,
-					"retrying sync send after timeout\n");
-			}
-			memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE);
-			ret = -ETIMEDOUT;
-		} else {
-			ret = 0;
-			break;
-		}
-	}
-
-	ctlr->received_resp = false;
-	return ret;
-}
-
-static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd, u32 timeout)
-{
-	int ret;
-	u8 buf[2] = {JC_OUTPUT_USB_CMD};
-
-	buf[1] = cmd;
-	ctlr->usb_ack_match = cmd;
-	ctlr->msg_type = JOYCON_MSG_TYPE_USB;
-	ret = joycon_hid_send_sync(ctlr, buf, sizeof(buf), timeout);
-	if (ret)
-		hid_dbg(ctlr->hdev, "send usb command failed; ret=%d\n", ret);
-	return ret;
-}
-
-static int joycon_send_subcmd(struct joycon_ctlr *ctlr,
-			      struct joycon_subcmd_request *subcmd,
-			      size_t data_len, u32 timeout)
-{
-	int ret;
-	unsigned long flags;
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	/*
-	 * If the controller has been removed, just return ENODEV so the LED
-	 * subsystem doesn't print invalid errors on removal.
-	 */
-	if (ctlr->ctlr_state == JOYCON_CTLR_STATE_REMOVED) {
-		spin_unlock_irqrestore(&ctlr->lock, flags);
-		return -ENODEV;
-	}
-	memcpy(subcmd->rumble_data, ctlr->rumble_data[ctlr->rumble_queue_tail],
-	       JC_RUMBLE_DATA_SIZE);
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	subcmd->output_id = JC_OUTPUT_RUMBLE_AND_SUBCMD;
-	subcmd->packet_num = ctlr->subcmd_num;
-	if (++ctlr->subcmd_num > 0xF)
-		ctlr->subcmd_num = 0;
-	ctlr->subcmd_ack_match = subcmd->subcmd_id;
-	ctlr->msg_type = JOYCON_MSG_TYPE_SUBCMD;
-
-	ret = joycon_hid_send_sync(ctlr, (u8 *)subcmd,
-				   sizeof(*subcmd) + data_len, timeout);
-	if (ret < 0)
-		hid_dbg(ctlr->hdev, "send subcommand failed; ret=%d\n", ret);
-	else
-		ret = 0;
-	return ret;
-}
-
-/* Supply nibbles for flash and on. Ones correspond to active */
-static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on)
-{
-	struct joycon_subcmd_request *req;
-	u8 buffer[sizeof(*req) + 1] = { 0 };
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_SET_PLAYER_LIGHTS;
-	req->data[0] = (flash << 4) | on;
-
-	hid_dbg(ctlr->hdev, "setting player leds\n");
-	return joycon_send_subcmd(ctlr, req, 1, HZ/4);
-}
-
-static int joycon_request_spi_flash_read(struct joycon_ctlr *ctlr,
-					 u32 start_addr, u8 size, u8 **reply)
-{
-	struct joycon_subcmd_request *req;
-	struct joycon_input_report *report;
-	u8 buffer[sizeof(*req) + 5] = { 0 };
-	u8 *data;
-	int ret;
-
-	if (!reply)
-		return -EINVAL;
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_SPI_FLASH_READ;
-	data = req->data;
-	put_unaligned_le32(start_addr, data);
-	data[4] = size;
-
-	hid_dbg(ctlr->hdev, "requesting SPI flash data\n");
-	ret = joycon_send_subcmd(ctlr, req, 5, HZ);
-	if (ret) {
-		hid_err(ctlr->hdev, "failed reading SPI flash; ret=%d\n", ret);
-	} else {
-		report = (struct joycon_input_report *)ctlr->input_buf;
-		/* The read data starts at the 6th byte */
-		*reply = &report->subcmd_reply.data[5];
-	}
-	return ret;
-}
-
-/*
- * User calibration's presence is denoted with a magic byte preceding it.
- * returns 0 if magic val is present, 1 if not present, < 0 on error
- */
-static int joycon_check_for_cal_magic(struct joycon_ctlr *ctlr, u32 flash_addr)
-{
-	int ret;
-	u8 *reply;
-
-	ret = joycon_request_spi_flash_read(ctlr, flash_addr,
-					    JC_CAL_USR_MAGIC_SIZE, &reply);
-	if (ret)
-		return ret;
-
-	return reply[0] != JC_CAL_USR_MAGIC_0 || reply[1] != JC_CAL_USR_MAGIC_1;
-}
-
-static int joycon_read_stick_calibration(struct joycon_ctlr *ctlr, u16 cal_addr,
-					 struct joycon_stick_cal *cal_x,
-					 struct joycon_stick_cal *cal_y,
-					 bool left_stick)
-{
-	s32 x_max_above;
-	s32 x_min_below;
-	s32 y_max_above;
-	s32 y_min_below;
-	u8 *raw_cal;
-	int ret;
-
-	ret = joycon_request_spi_flash_read(ctlr, cal_addr,
-					    JC_CAL_STICK_DATA_SIZE, &raw_cal);
-	if (ret)
-		return ret;
-
-	/* stick calibration parsing: note the order differs based on stick */
-	if (left_stick) {
-		x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0,
-						12);
-		y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4,
-						12);
-		cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0,
-						  12);
-		cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4,
-						  12);
-		x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0,
-						12);
-		y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4,
-						12);
-	} else {
-		cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0,
-						  12);
-		cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4,
-						  12);
-		x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0,
-						12);
-		y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4,
-						12);
-		x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0,
-						12);
-		y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4,
-						12);
-	}
-
-	cal_x->max = cal_x->center + x_max_above;
-	cal_x->min = cal_x->center - x_min_below;
-	cal_y->max = cal_y->center + y_max_above;
-	cal_y->min = cal_y->center - y_min_below;
-
-	return 0;
-}
-
-static const u16 DFLT_STICK_CAL_CEN = 2000;
-static const u16 DFLT_STICK_CAL_MAX = 3500;
-static const u16 DFLT_STICK_CAL_MIN = 500;
-static int joycon_request_calibration(struct joycon_ctlr *ctlr)
-{
-	u16 left_stick_addr = JC_CAL_FCT_DATA_LEFT_ADDR;
-	u16 right_stick_addr = JC_CAL_FCT_DATA_RIGHT_ADDR;
-	int ret;
-
-	hid_dbg(ctlr->hdev, "requesting cal data\n");
-
-	/* check if user stick calibrations are present */
-	if (!joycon_check_for_cal_magic(ctlr, JC_CAL_USR_LEFT_MAGIC_ADDR)) {
-		left_stick_addr = JC_CAL_USR_LEFT_DATA_ADDR;
-		hid_info(ctlr->hdev, "using user cal for left stick\n");
-	} else {
-		hid_info(ctlr->hdev, "using factory cal for left stick\n");
-	}
-	if (!joycon_check_for_cal_magic(ctlr, JC_CAL_USR_RIGHT_MAGIC_ADDR)) {
-		right_stick_addr = JC_CAL_USR_RIGHT_DATA_ADDR;
-		hid_info(ctlr->hdev, "using user cal for right stick\n");
-	} else {
-		hid_info(ctlr->hdev, "using factory cal for right stick\n");
-	}
-
-	/* read the left stick calibration data */
-	ret = joycon_read_stick_calibration(ctlr, left_stick_addr,
-					    &ctlr->left_stick_cal_x,
-					    &ctlr->left_stick_cal_y,
-					    true);
-	if (ret) {
-		hid_warn(ctlr->hdev,
-			 "Failed to read left stick cal, using dflts; e=%d\n",
-			 ret);
-
-		ctlr->left_stick_cal_x.center = DFLT_STICK_CAL_CEN;
-		ctlr->left_stick_cal_x.max = DFLT_STICK_CAL_MAX;
-		ctlr->left_stick_cal_x.min = DFLT_STICK_CAL_MIN;
-
-		ctlr->left_stick_cal_y.center = DFLT_STICK_CAL_CEN;
-		ctlr->left_stick_cal_y.max = DFLT_STICK_CAL_MAX;
-		ctlr->left_stick_cal_y.min = DFLT_STICK_CAL_MIN;
-	}
-
-	/* read the right stick calibration data */
-	ret = joycon_read_stick_calibration(ctlr, right_stick_addr,
-					    &ctlr->right_stick_cal_x,
-					    &ctlr->right_stick_cal_y,
-					    false);
-	if (ret) {
-		hid_warn(ctlr->hdev,
-			 "Failed to read right stick cal, using dflts; e=%d\n",
-			 ret);
-
-		ctlr->right_stick_cal_x.center = DFLT_STICK_CAL_CEN;
-		ctlr->right_stick_cal_x.max = DFLT_STICK_CAL_MAX;
-		ctlr->right_stick_cal_x.min = DFLT_STICK_CAL_MIN;
-
-		ctlr->right_stick_cal_y.center = DFLT_STICK_CAL_CEN;
-		ctlr->right_stick_cal_y.max = DFLT_STICK_CAL_MAX;
-		ctlr->right_stick_cal_y.min = DFLT_STICK_CAL_MIN;
-	}
-
-	hid_dbg(ctlr->hdev, "calibration:\n"
-			    "l_x_c=%d l_x_max=%d l_x_min=%d\n"
-			    "l_y_c=%d l_y_max=%d l_y_min=%d\n"
-			    "r_x_c=%d r_x_max=%d r_x_min=%d\n"
-			    "r_y_c=%d r_y_max=%d r_y_min=%d\n",
-			    ctlr->left_stick_cal_x.center,
-			    ctlr->left_stick_cal_x.max,
-			    ctlr->left_stick_cal_x.min,
-			    ctlr->left_stick_cal_y.center,
-			    ctlr->left_stick_cal_y.max,
-			    ctlr->left_stick_cal_y.min,
-			    ctlr->right_stick_cal_x.center,
-			    ctlr->right_stick_cal_x.max,
-			    ctlr->right_stick_cal_x.min,
-			    ctlr->right_stick_cal_y.center,
-			    ctlr->right_stick_cal_y.max,
-			    ctlr->right_stick_cal_y.min);
-
-	return 0;
-}
-
-/*
- * These divisors are calculated once rather than for each sample. They are only
- * dependent on the IMU calibration values. They are used when processing the
- * IMU input reports.
- */
-static void joycon_calc_imu_cal_divisors(struct joycon_ctlr *ctlr)
-{
-	int i;
-
-	for (i = 0; i < 3; i++) {
-		ctlr->imu_cal_accel_divisor[i] = ctlr->accel_cal.scale[i] -
-						ctlr->accel_cal.offset[i];
-		ctlr->imu_cal_gyro_divisor[i] = ctlr->gyro_cal.scale[i] -
-						ctlr->gyro_cal.offset[i];
-	}
-}
-
-static const s16 DFLT_ACCEL_OFFSET /*= 0*/;
-static const s16 DFLT_ACCEL_SCALE = 16384;
-static const s16 DFLT_GYRO_OFFSET /*= 0*/;
-static const s16 DFLT_GYRO_SCALE  = 13371;
-static int joycon_request_imu_calibration(struct joycon_ctlr *ctlr)
-{
-	u16 imu_cal_addr = JC_IMU_CAL_FCT_DATA_ADDR;
-	u8 *raw_cal;
-	int ret;
-	int i;
-
-	/* check if user calibration exists */
-	if (!joycon_check_for_cal_magic(ctlr, JC_IMU_CAL_USR_MAGIC_ADDR)) {
-		imu_cal_addr = JC_IMU_CAL_USR_DATA_ADDR;
-		hid_info(ctlr->hdev, "using user cal for IMU\n");
-	} else {
-		hid_info(ctlr->hdev, "using factory cal for IMU\n");
-	}
-
-	/* request IMU calibration data */
-	hid_dbg(ctlr->hdev, "requesting IMU cal data\n");
-	ret = joycon_request_spi_flash_read(ctlr, imu_cal_addr,
-					    JC_IMU_CAL_DATA_SIZE, &raw_cal);
-	if (ret) {
-		hid_warn(ctlr->hdev,
-			 "Failed to read IMU cal, using defaults; ret=%d\n",
-			 ret);
-
-		for (i = 0; i < 3; i++) {
-			ctlr->accel_cal.offset[i] = DFLT_ACCEL_OFFSET;
-			ctlr->accel_cal.scale[i] = DFLT_ACCEL_SCALE;
-			ctlr->gyro_cal.offset[i] = DFLT_GYRO_OFFSET;
-			ctlr->gyro_cal.scale[i] = DFLT_GYRO_SCALE;
-		}
-		joycon_calc_imu_cal_divisors(ctlr);
-		return ret;
-	}
-
-	/* IMU calibration parsing */
-	for (i = 0; i < 3; i++) {
-		int j = i * 2;
-
-		ctlr->accel_cal.offset[i] = get_unaligned_le16(raw_cal + j);
-		ctlr->accel_cal.scale[i] = get_unaligned_le16(raw_cal + j + 6);
-		ctlr->gyro_cal.offset[i] = get_unaligned_le16(raw_cal + j + 12);
-		ctlr->gyro_cal.scale[i] = get_unaligned_le16(raw_cal + j + 18);
-	}
-
-	joycon_calc_imu_cal_divisors(ctlr);
-
-	hid_dbg(ctlr->hdev, "IMU calibration:\n"
-			    "a_o[0]=%d a_o[1]=%d a_o[2]=%d\n"
-			    "a_s[0]=%d a_s[1]=%d a_s[2]=%d\n"
-			    "g_o[0]=%d g_o[1]=%d g_o[2]=%d\n"
-			    "g_s[0]=%d g_s[1]=%d g_s[2]=%d\n",
-			    ctlr->accel_cal.offset[0],
-			    ctlr->accel_cal.offset[1],
-			    ctlr->accel_cal.offset[2],
-			    ctlr->accel_cal.scale[0],
-			    ctlr->accel_cal.scale[1],
-			    ctlr->accel_cal.scale[2],
-			    ctlr->gyro_cal.offset[0],
-			    ctlr->gyro_cal.offset[1],
-			    ctlr->gyro_cal.offset[2],
-			    ctlr->gyro_cal.scale[0],
-			    ctlr->gyro_cal.scale[1],
-			    ctlr->gyro_cal.scale[2]);
-
-	return 0;
-}
-
-static int joycon_set_report_mode(struct joycon_ctlr *ctlr)
-{
-	struct joycon_subcmd_request *req;
-	u8 buffer[sizeof(*req) + 1] = { 0 };
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_SET_REPORT_MODE;
-	req->data[0] = 0x30; /* standard, full report mode */
-
-	hid_dbg(ctlr->hdev, "setting controller report mode\n");
-	return joycon_send_subcmd(ctlr, req, 1, HZ);
-}
-
-static int joycon_enable_rumble(struct joycon_ctlr *ctlr)
-{
-	struct joycon_subcmd_request *req;
-	u8 buffer[sizeof(*req) + 1] = { 0 };
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_ENABLE_VIBRATION;
-	req->data[0] = 0x01; /* note: 0x00 would disable */
-
-	hid_dbg(ctlr->hdev, "enabling rumble\n");
-	return joycon_send_subcmd(ctlr, req, 1, HZ/4);
-}
-
-static int joycon_enable_imu(struct joycon_ctlr *ctlr)
-{
-	struct joycon_subcmd_request *req;
-	u8 buffer[sizeof(*req) + 1] = { 0 };
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_ENABLE_IMU;
-	req->data[0] = 0x01; /* note: 0x00 would disable */
-
-	hid_dbg(ctlr->hdev, "enabling IMU\n");
-	return joycon_send_subcmd(ctlr, req, 1, HZ);
-}
-
-static s32 joycon_map_stick_val(struct joycon_stick_cal *cal, s32 val)
-{
-	s32 center = cal->center;
-	s32 min = cal->min;
-	s32 max = cal->max;
-	s32 new_val;
-
-	if (val > center) {
-		new_val = (val - center) * JC_MAX_STICK_MAG;
-		new_val /= (max - center);
-	} else {
-		new_val = (center - val) * -JC_MAX_STICK_MAG;
-		new_val /= (center - min);
-	}
-	new_val = clamp(new_val, (s32)-JC_MAX_STICK_MAG, (s32)JC_MAX_STICK_MAG);
-	return new_val;
-}
-
-static void joycon_input_report_parse_imu_data(struct joycon_ctlr *ctlr,
-					       struct joycon_input_report *rep,
-					       struct joycon_imu_data *imu_data)
-{
-	u8 *raw = rep->imu_raw_bytes;
-	int i;
-
-	for (i = 0; i < 3; i++) {
-		struct joycon_imu_data *data = &imu_data[i];
-
-		data->accel_x = get_unaligned_le16(raw + 0);
-		data->accel_y = get_unaligned_le16(raw + 2);
-		data->accel_z = get_unaligned_le16(raw + 4);
-		data->gyro_x = get_unaligned_le16(raw + 6);
-		data->gyro_y = get_unaligned_le16(raw + 8);
-		data->gyro_z = get_unaligned_le16(raw + 10);
-		/* point to next imu sample */
-		raw += sizeof(struct joycon_imu_data);
-	}
-}
-
-static void joycon_parse_imu_report(struct joycon_ctlr *ctlr,
-				    struct joycon_input_report *rep)
-{
-	struct joycon_imu_data imu_data[3] = {0}; /* 3 reports per packet */
-	struct input_dev *idev = ctlr->imu_input;
-	unsigned int msecs = jiffies_to_msecs(jiffies);
-	unsigned int last_msecs = ctlr->imu_last_pkt_ms;
-	int i;
-	int value[6];
-
-	joycon_input_report_parse_imu_data(ctlr, rep, imu_data);
-
-	/*
-	 * There are complexities surrounding how we determine the timestamps we
-	 * associate with the samples we pass to userspace. The IMU input
-	 * reports do not provide us with a good timestamp. There's a quickly
-	 * incrementing 8-bit counter per input report, but it is not very
-	 * useful for this purpose (it is not entirely clear what rate it
-	 * increments at or if it varies based on packet push rate - more on
-	 * the push rate below...).
-	 *
-	 * The reverse engineering work done on the joy-cons and pro controllers
-	 * by the community seems to indicate the following:
-	 * - The controller samples the IMU every 1.35ms. It then does some of
-	 *   its own processing, probably averaging the samples out.
-	 * - Each imu input report contains 3 IMU samples, (usually 5ms apart).
-	 * - In the standard reporting mode (which this driver uses exclusively)
-	 *   input reports are pushed from the controller as follows:
-	 *      * joy-con (bluetooth): every 15 ms
-	 *      * joy-cons (in charging grip via USB): every 15 ms
-	 *      * pro controller (USB): every 15 ms
-	 *      * pro controller (bluetooth): every 8 ms (this is the wildcard)
-	 *
-	 * Further complicating matters is that some bluetooth stacks are known
-	 * to alter the controller's packet rate by hardcoding the bluetooth
-	 * SSR for the switch controllers (android's stack currently sets the
-	 * SSR to 11ms for both the joy-cons and pro controllers).
-	 *
-	 * In my own testing, I've discovered that my pro controller either
-	 * reports IMU sample batches every 11ms or every 15ms. This rate is
-	 * stable after connecting. It isn't 100% clear what determines this
-	 * rate. Importantly, even when sending every 11ms, none of the samples
-	 * are duplicates. This seems to indicate that the time deltas between
-	 * reported samples can vary based on the input report rate.
-	 *
-	 * The solution employed in this driver is to keep track of the average
-	 * time delta between IMU input reports. In testing, this value has
-	 * proven to be stable, staying at 15ms or 11ms, though other hardware
-	 * configurations and bluetooth stacks could potentially see other rates
-	 * (hopefully this will become more clear as more people use the
-	 * driver).
-	 *
-	 * Keeping track of the average report delta allows us to submit our
-	 * timestamps to userspace based on that. Each report contains 3
-	 * samples, so the IMU sampling rate should be avg_time_delta/3. We can
-	 * also use this average to detect events where we have dropped a
-	 * packet. The userspace timestamp for the samples will be adjusted
-	 * accordingly to prevent unwanted behvaior.
-	 */
-	if (!ctlr->imu_first_packet_received) {
-		ctlr->imu_timestamp_us = 0;
-		ctlr->imu_delta_samples_count = 0;
-		ctlr->imu_delta_samples_sum = 0;
-		ctlr->imu_avg_delta_ms = JC_IMU_DFLT_AVG_DELTA_MS;
-		ctlr->imu_first_packet_received = true;
-	} else {
-		unsigned int delta = msecs - last_msecs;
-		unsigned int dropped_pkts;
-		unsigned int dropped_threshold;
-
-		/* avg imu report delta housekeeping */
-		ctlr->imu_delta_samples_sum += delta;
-		ctlr->imu_delta_samples_count++;
-		if (ctlr->imu_delta_samples_count >=
-		    JC_IMU_SAMPLES_PER_DELTA_AVG) {
-			ctlr->imu_avg_delta_ms = ctlr->imu_delta_samples_sum /
-						 ctlr->imu_delta_samples_count;
-			/* don't ever want divide by zero shenanigans */
-			if (ctlr->imu_avg_delta_ms == 0) {
-				ctlr->imu_avg_delta_ms = 1;
-				hid_warn(ctlr->hdev,
-					 "calculated avg imu delta of 0\n");
-			}
-			ctlr->imu_delta_samples_count = 0;
-			ctlr->imu_delta_samples_sum = 0;
-		}
-
-		/* useful for debugging IMU sample rate */
-		hid_dbg(ctlr->hdev,
-			"imu_report: ms=%u last_ms=%u delta=%u avg_delta=%u\n",
-			msecs, last_msecs, delta, ctlr->imu_avg_delta_ms);
-
-		/* check if any packets have been dropped */
-		dropped_threshold = ctlr->imu_avg_delta_ms * 3 / 2;
-		dropped_pkts = (delta - min(delta, dropped_threshold)) /
-				ctlr->imu_avg_delta_ms;
-		ctlr->imu_timestamp_us += 1000 * ctlr->imu_avg_delta_ms;
-		if (dropped_pkts > JC_IMU_DROPPED_PKT_WARNING) {
-			hid_warn(ctlr->hdev,
-				 "compensating for %u dropped IMU reports\n",
-				 dropped_pkts);
-			hid_warn(ctlr->hdev,
-				 "delta=%u avg_delta=%u\n",
-				 delta, ctlr->imu_avg_delta_ms);
-		}
-	}
-	ctlr->imu_last_pkt_ms = msecs;
-
-	/* Each IMU input report contains three samples */
-	for (i = 0; i < 3; i++) {
-		input_event(idev, EV_MSC, MSC_TIMESTAMP,
-			    ctlr->imu_timestamp_us);
-
-		/*
-		 * These calculations (which use the controller's calibration
-		 * settings to improve the final values) are based on those
-		 * found in the community's reverse-engineering repo (linked at
-		 * top of driver). For hid-nintendo, we make sure that the final
-		 * value given to userspace is always in terms of the axis
-		 * resolution we provided.
-		 *
-		 * Currently only the gyro calculations subtract the calibration
-		 * offsets from the raw value itself. In testing, doing the same
-		 * for the accelerometer raw values decreased accuracy.
-		 *
-		 * Note that the gyro values are multiplied by the
-		 * precision-saving scaling factor to prevent large inaccuracies
-		 * due to truncation of the resolution value which would
-		 * otherwise occur. To prevent overflow (without resorting to 64
-		 * bit integer math), the mult_frac macro is used.
-		 */
-		value[0] = mult_frac((JC_IMU_PREC_RANGE_SCALE *
-				      (imu_data[i].gyro_x -
-				       ctlr->gyro_cal.offset[0])),
-				     ctlr->gyro_cal.scale[0],
-				     ctlr->imu_cal_gyro_divisor[0]);
-		value[1] = mult_frac((JC_IMU_PREC_RANGE_SCALE *
-				      (imu_data[i].gyro_y -
-				       ctlr->gyro_cal.offset[1])),
-				     ctlr->gyro_cal.scale[1],
-				     ctlr->imu_cal_gyro_divisor[1]);
-		value[2] = mult_frac((JC_IMU_PREC_RANGE_SCALE *
-				      (imu_data[i].gyro_z -
-				       ctlr->gyro_cal.offset[2])),
-				     ctlr->gyro_cal.scale[2],
-				     ctlr->imu_cal_gyro_divisor[2]);
-
-		value[3] = ((s32)imu_data[i].accel_x *
-			    ctlr->accel_cal.scale[0]) /
-			    ctlr->imu_cal_accel_divisor[0];
-		value[4] = ((s32)imu_data[i].accel_y *
-			    ctlr->accel_cal.scale[1]) /
-			    ctlr->imu_cal_accel_divisor[1];
-		value[5] = ((s32)imu_data[i].accel_z *
-			    ctlr->accel_cal.scale[2]) /
-			    ctlr->imu_cal_accel_divisor[2];
-
-		hid_dbg(ctlr->hdev, "raw_gyro: g_x=%d g_y=%d g_z=%d\n",
-			imu_data[i].gyro_x, imu_data[i].gyro_y,
-			imu_data[i].gyro_z);
-		hid_dbg(ctlr->hdev, "raw_accel: a_x=%d a_y=%d a_z=%d\n",
-			imu_data[i].accel_x, imu_data[i].accel_y,
-			imu_data[i].accel_z);
-
-		/*
-		 * The right joy-con has 2 axes negated, Y and Z. This is due to
-		 * the orientation of the IMU in the controller. We negate those
-		 * axes' values in order to be consistent with the left joy-con
-		 * and the pro controller:
-		 *   X: positive is pointing toward the triggers
-		 *   Y: positive is pointing to the left
-		 *   Z: positive is pointing up (out of the buttons/sticks)
-		 * The axes follow the right-hand rule.
-		 */
-		if (jc_type_is_joycon(ctlr) && jc_type_has_right(ctlr)) {
-			int j;
-
-			/* negate all but x axis */
-			for (j = 1; j < 6; ++j) {
-				if (j == 3)
-					continue;
-				value[j] *= -1;
-			}
-		}
-
-		input_report_abs(idev, ABS_RX, value[0]);
-		input_report_abs(idev, ABS_RY, value[1]);
-		input_report_abs(idev, ABS_RZ, value[2]);
-		input_report_abs(idev, ABS_X, value[3]);
-		input_report_abs(idev, ABS_Y, value[4]);
-		input_report_abs(idev, ABS_Z, value[5]);
-		input_sync(idev);
-		/* convert to micros and divide by 3 (3 samples per report). */
-		ctlr->imu_timestamp_us += ctlr->imu_avg_delta_ms * 1000 / 3;
-	}
-}
-
-static void joycon_parse_report(struct joycon_ctlr *ctlr,
-				struct joycon_input_report *rep)
-{
-	struct input_dev *dev = ctlr->input;
-	unsigned long flags;
-	u8 tmp;
-	u32 btns;
-	unsigned long msecs = jiffies_to_msecs(jiffies);
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	if (IS_ENABLED(CONFIG_NINTENDO_FF) && rep->vibrator_report &&
-	    (msecs - ctlr->rumble_msecs) >= JC_RUMBLE_PERIOD_MS &&
-	    (ctlr->rumble_queue_head != ctlr->rumble_queue_tail ||
-	     ctlr->rumble_zero_countdown > 0)) {
-		/*
-		 * When this value reaches 0, we know we've sent multiple
-		 * packets to the controller instructing it to disable rumble.
-		 * We can safely stop sending periodic rumble packets until the
-		 * next ff effect.
-		 */
-		if (ctlr->rumble_zero_countdown > 0)
-			ctlr->rumble_zero_countdown--;
-		queue_work(ctlr->rumble_queue, &ctlr->rumble_worker);
-	}
-
-	/* Parse the battery status */
-	tmp = rep->bat_con;
-	ctlr->host_powered = tmp & BIT(0);
-	ctlr->battery_charging = tmp & BIT(4);
-	tmp = tmp >> 5;
-	switch (tmp) {
-	case 0: /* empty */
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
-		break;
-	case 1: /* low */
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-		break;
-	case 2: /* medium */
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-		break;
-	case 3: /* high */
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
-		break;
-	case 4: /* full */
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-		break;
-	default:
-		ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
-		hid_warn(ctlr->hdev, "Invalid battery status\n");
-		break;
-	}
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	/* Parse the buttons and sticks */
-	btns = hid_field_extract(ctlr->hdev, rep->button_status, 0, 24);
-
-	if (jc_type_has_left(ctlr)) {
-		u16 raw_x;
-		u16 raw_y;
-		s32 x;
-		s32 y;
-
-		/* get raw stick values */
-		raw_x = hid_field_extract(ctlr->hdev, rep->left_stick, 0, 12);
-		raw_y = hid_field_extract(ctlr->hdev,
-					  rep->left_stick + 1, 4, 12);
-		/* map the stick values */
-		x = joycon_map_stick_val(&ctlr->left_stick_cal_x, raw_x);
-		y = -joycon_map_stick_val(&ctlr->left_stick_cal_y, raw_y);
-		/* report sticks */
-		input_report_abs(dev, ABS_X, x);
-		input_report_abs(dev, ABS_Y, y);
-
-		/* report buttons */
-		input_report_key(dev, BTN_TL, btns & JC_BTN_L);
-		input_report_key(dev, BTN_TL2, btns & JC_BTN_ZL);
-		input_report_key(dev, BTN_SELECT, btns & JC_BTN_MINUS);
-		input_report_key(dev, BTN_THUMBL, btns & JC_BTN_LSTICK);
-		input_report_key(dev, BTN_Z, btns & JC_BTN_CAP);
-
-		if (jc_type_is_joycon(ctlr)) {
-			/* Report the S buttons as the non-existent triggers */
-			input_report_key(dev, BTN_TR, btns & JC_BTN_SL_L);
-			input_report_key(dev, BTN_TR2, btns & JC_BTN_SR_L);
-
-			/* Report d-pad as digital buttons for the joy-cons */
-			input_report_key(dev, BTN_DPAD_DOWN,
-					 btns & JC_BTN_DOWN);
-			input_report_key(dev, BTN_DPAD_UP, btns & JC_BTN_UP);
-			input_report_key(dev, BTN_DPAD_RIGHT,
-					 btns & JC_BTN_RIGHT);
-			input_report_key(dev, BTN_DPAD_LEFT,
-					 btns & JC_BTN_LEFT);
-		} else {
-			int hatx = 0;
-			int haty = 0;
-
-			/* d-pad x */
-			if (btns & JC_BTN_LEFT)
-				hatx = -1;
-			else if (btns & JC_BTN_RIGHT)
-				hatx = 1;
-			input_report_abs(dev, ABS_HAT0X, hatx);
-
-			/* d-pad y */
-			if (btns & JC_BTN_UP)
-				haty = -1;
-			else if (btns & JC_BTN_DOWN)
-				haty = 1;
-			input_report_abs(dev, ABS_HAT0Y, haty);
-		}
-	}
-	if (jc_type_has_right(ctlr)) {
-		u16 raw_x;
-		u16 raw_y;
-		s32 x;
-		s32 y;
-
-		/* get raw stick values */
-		raw_x = hid_field_extract(ctlr->hdev, rep->right_stick, 0, 12);
-		raw_y = hid_field_extract(ctlr->hdev,
-					  rep->right_stick + 1, 4, 12);
-		/* map stick values */
-		x = joycon_map_stick_val(&ctlr->right_stick_cal_x, raw_x);
-		y = -joycon_map_stick_val(&ctlr->right_stick_cal_y, raw_y);
-		/* report sticks */
-		input_report_abs(dev, ABS_RX, x);
-		input_report_abs(dev, ABS_RY, y);
-
-		/* report buttons */
-		input_report_key(dev, BTN_TR, btns & JC_BTN_R);
-		input_report_key(dev, BTN_TR2, btns & JC_BTN_ZR);
-		if (jc_type_is_joycon(ctlr)) {
-			/* Report the S buttons as the non-existent triggers */
-			input_report_key(dev, BTN_TL, btns & JC_BTN_SL_R);
-			input_report_key(dev, BTN_TL2, btns & JC_BTN_SR_R);
-		}
-		input_report_key(dev, BTN_START, btns & JC_BTN_PLUS);
-		input_report_key(dev, BTN_THUMBR, btns & JC_BTN_RSTICK);
-		input_report_key(dev, BTN_MODE, btns & JC_BTN_HOME);
-		input_report_key(dev, BTN_WEST, btns & JC_BTN_Y);
-		input_report_key(dev, BTN_NORTH, btns & JC_BTN_X);
-		input_report_key(dev, BTN_EAST, btns & JC_BTN_A);
-		input_report_key(dev, BTN_SOUTH, btns & JC_BTN_B);
-	}
-
-	input_sync(dev);
-
-	/*
-	 * Immediately after receiving a report is the most reliable time to
-	 * send a subcommand to the controller. Wake any subcommand senders
-	 * waiting for a report.
-	 */
-	if (unlikely(mutex_is_locked(&ctlr->output_mutex))) {
-		spin_lock_irqsave(&ctlr->lock, flags);
-		ctlr->received_input_report = true;
-		spin_unlock_irqrestore(&ctlr->lock, flags);
-		wake_up(&ctlr->wait);
-	}
-
-	/* parse IMU data if present */
-	if (rep->id == JC_INPUT_IMU_DATA)
-		joycon_parse_imu_report(ctlr, rep);
-}
-
-static int joycon_send_rumble_data(struct joycon_ctlr *ctlr)
-{
-	int ret;
-	unsigned long flags;
-	struct joycon_rumble_output rumble_output = { 0 };
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	/*
-	 * If the controller has been removed, just return ENODEV so the LED
-	 * subsystem doesn't print invalid errors on removal.
-	 */
-	if (ctlr->ctlr_state == JOYCON_CTLR_STATE_REMOVED) {
-		spin_unlock_irqrestore(&ctlr->lock, flags);
-		return -ENODEV;
-	}
-	memcpy(rumble_output.rumble_data,
-	       ctlr->rumble_data[ctlr->rumble_queue_tail],
-	       JC_RUMBLE_DATA_SIZE);
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	rumble_output.output_id = JC_OUTPUT_RUMBLE_ONLY;
-	rumble_output.packet_num = ctlr->subcmd_num;
-	if (++ctlr->subcmd_num > 0xF)
-		ctlr->subcmd_num = 0;
-
-	joycon_enforce_subcmd_rate(ctlr);
-
-	ret = __joycon_hid_send(ctlr->hdev, (u8 *)&rumble_output,
-				sizeof(rumble_output));
-	return ret;
-}
-
-static void joycon_rumble_worker(struct work_struct *work)
-{
-	struct joycon_ctlr *ctlr = container_of(work, struct joycon_ctlr,
-							rumble_worker);
-	unsigned long flags;
-	bool again = true;
-	int ret;
-
-	while (again) {
-		mutex_lock(&ctlr->output_mutex);
-		ret = joycon_send_rumble_data(ctlr);
-		mutex_unlock(&ctlr->output_mutex);
-
-		/* -ENODEV means the controller was just unplugged */
-		spin_lock_irqsave(&ctlr->lock, flags);
-		if (ret < 0 && ret != -ENODEV &&
-		    ctlr->ctlr_state != JOYCON_CTLR_STATE_REMOVED)
-			hid_warn(ctlr->hdev, "Failed to set rumble; e=%d", ret);
-
-		ctlr->rumble_msecs = jiffies_to_msecs(jiffies);
-		if (ctlr->rumble_queue_tail != ctlr->rumble_queue_head) {
-			if (++ctlr->rumble_queue_tail >= JC_RUMBLE_QUEUE_SIZE)
-				ctlr->rumble_queue_tail = 0;
-		} else {
-			again = false;
-		}
-		spin_unlock_irqrestore(&ctlr->lock, flags);
-	}
-}
-
-#if IS_ENABLED(CONFIG_NINTENDO_FF)
-static struct joycon_rumble_freq_data joycon_find_rumble_freq(u16 freq)
-{
-	const size_t length = ARRAY_SIZE(joycon_rumble_frequencies);
-	const struct joycon_rumble_freq_data *data = joycon_rumble_frequencies;
-	int i = 0;
-
-	if (freq > data[0].freq) {
-		for (i = 1; i < length - 1; i++) {
-			if (freq > data[i - 1].freq && freq <= data[i].freq)
-				break;
-		}
-	}
-
-	return data[i];
-}
-
-static struct joycon_rumble_amp_data joycon_find_rumble_amp(u16 amp)
-{
-	const size_t length = ARRAY_SIZE(joycon_rumble_amplitudes);
-	const struct joycon_rumble_amp_data *data = joycon_rumble_amplitudes;
-	int i = 0;
-
-	if (amp > data[0].amp) {
-		for (i = 1; i < length - 1; i++) {
-			if (amp > data[i - 1].amp && amp <= data[i].amp)
-				break;
-		}
-	}
-
-	return data[i];
-}
-
-static void joycon_encode_rumble(u8 *data, u16 freq_low, u16 freq_high, u16 amp)
-{
-	struct joycon_rumble_freq_data freq_data_low;
-	struct joycon_rumble_freq_data freq_data_high;
-	struct joycon_rumble_amp_data amp_data;
-
-	freq_data_low = joycon_find_rumble_freq(freq_low);
-	freq_data_high = joycon_find_rumble_freq(freq_high);
-	amp_data = joycon_find_rumble_amp(amp);
-
-	data[0] = (freq_data_high.high >> 8) & 0xFF;
-	data[1] = (freq_data_high.high & 0xFF) + amp_data.high;
-	data[2] = freq_data_low.low + ((amp_data.low >> 8) & 0xFF);
-	data[3] = amp_data.low & 0xFF;
-}
-
-static const u16 JOYCON_MAX_RUMBLE_HIGH_FREQ	= 1253;
-static const u16 JOYCON_MIN_RUMBLE_HIGH_FREQ	= 82;
-static const u16 JOYCON_MAX_RUMBLE_LOW_FREQ	= 626;
-static const u16 JOYCON_MIN_RUMBLE_LOW_FREQ	= 41;
-
-static void joycon_clamp_rumble_freqs(struct joycon_ctlr *ctlr)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	ctlr->rumble_ll_freq = clamp(ctlr->rumble_ll_freq,
-				     JOYCON_MIN_RUMBLE_LOW_FREQ,
-				     JOYCON_MAX_RUMBLE_LOW_FREQ);
-	ctlr->rumble_lh_freq = clamp(ctlr->rumble_lh_freq,
-				     JOYCON_MIN_RUMBLE_HIGH_FREQ,
-				     JOYCON_MAX_RUMBLE_HIGH_FREQ);
-	ctlr->rumble_rl_freq = clamp(ctlr->rumble_rl_freq,
-				     JOYCON_MIN_RUMBLE_LOW_FREQ,
-				     JOYCON_MAX_RUMBLE_LOW_FREQ);
-	ctlr->rumble_rh_freq = clamp(ctlr->rumble_rh_freq,
-				     JOYCON_MIN_RUMBLE_HIGH_FREQ,
-				     JOYCON_MAX_RUMBLE_HIGH_FREQ);
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-}
-
-static int joycon_set_rumble(struct joycon_ctlr *ctlr, u16 amp_r, u16 amp_l,
-			     bool schedule_now)
-{
-	u8 data[JC_RUMBLE_DATA_SIZE];
-	u16 amp;
-	u16 freq_r_low;
-	u16 freq_r_high;
-	u16 freq_l_low;
-	u16 freq_l_high;
-	unsigned long flags;
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	freq_r_low = ctlr->rumble_rl_freq;
-	freq_r_high = ctlr->rumble_rh_freq;
-	freq_l_low = ctlr->rumble_ll_freq;
-	freq_l_high = ctlr->rumble_lh_freq;
-	/* limit number of silent rumble packets to reduce traffic */
-	if (amp_l != 0 || amp_r != 0)
-		ctlr->rumble_zero_countdown = JC_RUMBLE_ZERO_AMP_PKT_CNT;
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	/* right joy-con */
-	amp = amp_r * (u32)joycon_max_rumble_amp / 65535;
-	joycon_encode_rumble(data + 4, freq_r_low, freq_r_high, amp);
-
-	/* left joy-con */
-	amp = amp_l * (u32)joycon_max_rumble_amp / 65535;
-	joycon_encode_rumble(data, freq_l_low, freq_l_high, amp);
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	if (++ctlr->rumble_queue_head >= JC_RUMBLE_QUEUE_SIZE)
-		ctlr->rumble_queue_head = 0;
-	memcpy(ctlr->rumble_data[ctlr->rumble_queue_head], data,
-	       JC_RUMBLE_DATA_SIZE);
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	/* don't wait for the periodic send (reduces latency) */
-	if (schedule_now)
-		queue_work(ctlr->rumble_queue, &ctlr->rumble_worker);
-
-	return 0;
-}
-
-static int joycon_play_effect(struct input_dev *dev, void *data,
-						     struct ff_effect *effect)
-{
-	struct joycon_ctlr *ctlr = input_get_drvdata(dev);
-
-	if (effect->type != FF_RUMBLE)
-		return 0;
-
-	return joycon_set_rumble(ctlr,
-				 effect->u.rumble.weak_magnitude,
-				 effect->u.rumble.strong_magnitude,
-				 true);
-}
-#endif /* IS_ENABLED(CONFIG_NINTENDO_FF) */
-
-static const unsigned int joycon_button_inputs_l[] = {
-	BTN_SELECT, BTN_Z, BTN_THUMBL,
-	BTN_TL, BTN_TL2,
-	0 /* 0 signals end of array */
-};
-
-static const unsigned int joycon_button_inputs_r[] = {
-	BTN_START, BTN_MODE, BTN_THUMBR,
-	BTN_SOUTH, BTN_EAST, BTN_NORTH, BTN_WEST,
-	BTN_TR, BTN_TR2,
-	0 /* 0 signals end of array */
-};
-
-/* We report joy-con d-pad inputs as buttons and pro controller as a hat. */
-static const unsigned int joycon_dpad_inputs_jc[] = {
-	BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT,
-};
-
-static int joycon_input_create(struct joycon_ctlr *ctlr)
-{
-	struct hid_device *hdev;
-	const char *name;
-	const char *imu_name;
-	int ret;
-	int i;
-
-	hdev = ctlr->hdev;
-
-	switch (hdev->product) {
-	case USB_DEVICE_ID_NINTENDO_PROCON:
-		name = "Nintendo Switch Pro Controller";
-		imu_name = "Nintendo Switch Pro Controller IMU";
-		break;
-	case USB_DEVICE_ID_NINTENDO_CHRGGRIP:
-		if (jc_type_has_left(ctlr)) {
-			name = "Nintendo Switch Left Joy-Con (Grip)";
-			imu_name = "Nintendo Switch Left Joy-Con IMU (Grip)";
-		} else {
-			name = "Nintendo Switch Right Joy-Con (Grip)";
-			imu_name = "Nintendo Switch Right Joy-Con IMU (Grip)";
-		}
-		break;
-	case USB_DEVICE_ID_NINTENDO_JOYCONL:
-		name = "Nintendo Switch Left Joy-Con";
-		imu_name = "Nintendo Switch Left Joy-Con IMU";
-		break;
-	case USB_DEVICE_ID_NINTENDO_JOYCONR:
-		name = "Nintendo Switch Right Joy-Con";
-		imu_name = "Nintendo Switch Right Joy-Con IMU";
-		break;
-	default: /* Should be impossible */
-		hid_err(hdev, "Invalid hid product\n");
-		return -EINVAL;
-	}
-
-	ctlr->input = devm_input_allocate_device(&hdev->dev);
-	if (!ctlr->input)
-		return -ENOMEM;
-	ctlr->input->id.bustype = hdev->bus;
-	ctlr->input->id.vendor = hdev->vendor;
-	ctlr->input->id.product = hdev->product;
-	ctlr->input->id.version = hdev->version;
-	ctlr->input->uniq = ctlr->mac_addr_str;
-	ctlr->input->name = name;
-	input_set_drvdata(ctlr->input, ctlr);
-
-	/* set up sticks and buttons */
-	if (jc_type_has_left(ctlr)) {
-		input_set_abs_params(ctlr->input, ABS_X,
-				     -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
-				     JC_STICK_FUZZ, JC_STICK_FLAT);
-		input_set_abs_params(ctlr->input, ABS_Y,
-				     -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
-				     JC_STICK_FUZZ, JC_STICK_FLAT);
-
-		for (i = 0; joycon_button_inputs_l[i] > 0; i++)
-			input_set_capability(ctlr->input, EV_KEY,
-					     joycon_button_inputs_l[i]);
-
-		/* configure d-pad differently for joy-con vs pro controller */
-		if (hdev->product != USB_DEVICE_ID_NINTENDO_PROCON) {
-			for (i = 0; joycon_dpad_inputs_jc[i] > 0; i++)
-				input_set_capability(ctlr->input, EV_KEY,
-						     joycon_dpad_inputs_jc[i]);
-		} else {
-			input_set_abs_params(ctlr->input, ABS_HAT0X,
-					     -JC_MAX_DPAD_MAG, JC_MAX_DPAD_MAG,
-					     JC_DPAD_FUZZ, JC_DPAD_FLAT);
-			input_set_abs_params(ctlr->input, ABS_HAT0Y,
-					     -JC_MAX_DPAD_MAG, JC_MAX_DPAD_MAG,
-					     JC_DPAD_FUZZ, JC_DPAD_FLAT);
-		}
-	}
-	if (jc_type_has_right(ctlr)) {
-		input_set_abs_params(ctlr->input, ABS_RX,
-				     -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
-				     JC_STICK_FUZZ, JC_STICK_FLAT);
-		input_set_abs_params(ctlr->input, ABS_RY,
-				     -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG,
-				     JC_STICK_FUZZ, JC_STICK_FLAT);
-
-		for (i = 0; joycon_button_inputs_r[i] > 0; i++)
-			input_set_capability(ctlr->input, EV_KEY,
-					     joycon_button_inputs_r[i]);
-	}
-
-	/* Let's report joy-con S triggers separately */
-	if (hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONL) {
-		input_set_capability(ctlr->input, EV_KEY, BTN_TR);
-		input_set_capability(ctlr->input, EV_KEY, BTN_TR2);
-	} else if (hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONR) {
-		input_set_capability(ctlr->input, EV_KEY, BTN_TL);
-		input_set_capability(ctlr->input, EV_KEY, BTN_TL2);
-	}
-
-#if IS_ENABLED(CONFIG_NINTENDO_FF)
-	/* set up rumble */
-	input_set_capability(ctlr->input, EV_FF, FF_RUMBLE);
-	input_ff_create_memless(ctlr->input, NULL, joycon_play_effect);
-	ctlr->rumble_ll_freq = JC_RUMBLE_DFLT_LOW_FREQ;
-	ctlr->rumble_lh_freq = JC_RUMBLE_DFLT_HIGH_FREQ;
-	ctlr->rumble_rl_freq = JC_RUMBLE_DFLT_LOW_FREQ;
-	ctlr->rumble_rh_freq = JC_RUMBLE_DFLT_HIGH_FREQ;
-	joycon_clamp_rumble_freqs(ctlr);
-	joycon_set_rumble(ctlr, 0, 0, false);
-	ctlr->rumble_msecs = jiffies_to_msecs(jiffies);
-#endif
-
-	ret = input_register_device(ctlr->input);
-	if (ret)
-		return ret;
-
-	/* configure the imu input device */
-	ctlr->imu_input = devm_input_allocate_device(&hdev->dev);
-	if (!ctlr->imu_input)
-		return -ENOMEM;
-
-	ctlr->imu_input->id.bustype = hdev->bus;
-	ctlr->imu_input->id.vendor = hdev->vendor;
-	ctlr->imu_input->id.product = hdev->product;
-	ctlr->imu_input->id.version = hdev->version;
-	ctlr->imu_input->uniq = ctlr->mac_addr_str;
-	ctlr->imu_input->name = imu_name;
-	input_set_drvdata(ctlr->imu_input, ctlr);
-
-	/* configure imu axes */
-	input_set_abs_params(ctlr->imu_input, ABS_X,
-			     -JC_IMU_MAX_ACCEL_MAG, JC_IMU_MAX_ACCEL_MAG,
-			     JC_IMU_ACCEL_FUZZ, JC_IMU_ACCEL_FLAT);
-	input_set_abs_params(ctlr->imu_input, ABS_Y,
-			     -JC_IMU_MAX_ACCEL_MAG, JC_IMU_MAX_ACCEL_MAG,
-			     JC_IMU_ACCEL_FUZZ, JC_IMU_ACCEL_FLAT);
-	input_set_abs_params(ctlr->imu_input, ABS_Z,
-			     -JC_IMU_MAX_ACCEL_MAG, JC_IMU_MAX_ACCEL_MAG,
-			     JC_IMU_ACCEL_FUZZ, JC_IMU_ACCEL_FLAT);
-	input_abs_set_res(ctlr->imu_input, ABS_X, JC_IMU_ACCEL_RES_PER_G);
-	input_abs_set_res(ctlr->imu_input, ABS_Y, JC_IMU_ACCEL_RES_PER_G);
-	input_abs_set_res(ctlr->imu_input, ABS_Z, JC_IMU_ACCEL_RES_PER_G);
-
-	input_set_abs_params(ctlr->imu_input, ABS_RX,
-			     -JC_IMU_MAX_GYRO_MAG, JC_IMU_MAX_GYRO_MAG,
-			     JC_IMU_GYRO_FUZZ, JC_IMU_GYRO_FLAT);
-	input_set_abs_params(ctlr->imu_input, ABS_RY,
-			     -JC_IMU_MAX_GYRO_MAG, JC_IMU_MAX_GYRO_MAG,
-			     JC_IMU_GYRO_FUZZ, JC_IMU_GYRO_FLAT);
-	input_set_abs_params(ctlr->imu_input, ABS_RZ,
-			     -JC_IMU_MAX_GYRO_MAG, JC_IMU_MAX_GYRO_MAG,
-			     JC_IMU_GYRO_FUZZ, JC_IMU_GYRO_FLAT);
-
-	input_abs_set_res(ctlr->imu_input, ABS_RX, JC_IMU_GYRO_RES_PER_DPS);
-	input_abs_set_res(ctlr->imu_input, ABS_RY, JC_IMU_GYRO_RES_PER_DPS);
-	input_abs_set_res(ctlr->imu_input, ABS_RZ, JC_IMU_GYRO_RES_PER_DPS);
-
-	__set_bit(EV_MSC, ctlr->imu_input->evbit);
-	__set_bit(MSC_TIMESTAMP, ctlr->imu_input->mscbit);
-	__set_bit(INPUT_PROP_ACCELEROMETER, ctlr->imu_input->propbit);
-
-	ret = input_register_device(ctlr->imu_input);
-	if (ret)
-		return ret;
-
-	return 0;
-}
-
-static int joycon_player_led_brightness_set(struct led_classdev *led,
-					    enum led_brightness brightness)
-{
-	struct device *dev = led->dev->parent;
-	struct hid_device *hdev = to_hid_device(dev);
-	struct joycon_ctlr *ctlr;
-	int val = 0;
-	int i;
-	int ret;
-	int num;
-
-	ctlr = hid_get_drvdata(hdev);
-	if (!ctlr) {
-		hid_err(hdev, "No controller data\n");
-		return -ENODEV;
-	}
-
-	/* determine which player led this is */
-	for (num = 0; num < JC_NUM_LEDS; num++) {
-		if (&ctlr->leds[num] == led)
-			break;
-	}
-	if (num >= JC_NUM_LEDS)
-		return -EINVAL;
-
-	mutex_lock(&ctlr->output_mutex);
-	for (i = 0; i < JC_NUM_LEDS; i++) {
-		if (i == num)
-			val |= brightness << i;
-		else
-			val |= ctlr->leds[i].brightness << i;
-	}
-	ret = joycon_set_player_leds(ctlr, 0, val);
-	mutex_unlock(&ctlr->output_mutex);
-
-	return ret;
-}
-
-static int joycon_home_led_brightness_set(struct led_classdev *led,
-					  enum led_brightness brightness)
-{
-	struct device *dev = led->dev->parent;
-	struct hid_device *hdev = to_hid_device(dev);
-	struct joycon_ctlr *ctlr;
-	struct joycon_subcmd_request *req;
-	u8 buffer[sizeof(*req) + 5] = { 0 };
-	u8 *data;
-	int ret;
-
-	ctlr = hid_get_drvdata(hdev);
-	if (!ctlr) {
-		hid_err(hdev, "No controller data\n");
-		return -ENODEV;
-	}
-
-	req = (struct joycon_subcmd_request *)buffer;
-	req->subcmd_id = JC_SUBCMD_SET_HOME_LIGHT;
-	data = req->data;
-	data[0] = 0x01;
-	data[1] = brightness << 4;
-	data[2] = brightness | (brightness << 4);
-	data[3] = 0x11;
-	data[4] = 0x11;
-
-	hid_dbg(hdev, "setting home led brightness\n");
-	mutex_lock(&ctlr->output_mutex);
-	ret = joycon_send_subcmd(ctlr, req, 5, HZ/4);
-	mutex_unlock(&ctlr->output_mutex);
-
-	return ret;
-}
-
-static DEFINE_MUTEX(joycon_input_num_mutex);
-static int joycon_leds_create(struct joycon_ctlr *ctlr)
-{
-	struct hid_device *hdev = ctlr->hdev;
-	struct device *dev = &hdev->dev;
-	const char *d_name = dev_name(dev);
-	struct led_classdev *led;
-	char *name;
-	int ret = 0;
-	int i;
-	static int input_num = 1;
-
-	/* Set the default controller player leds based on controller number */
-	mutex_lock(&joycon_input_num_mutex);
-	mutex_lock(&ctlr->output_mutex);
-	ret = joycon_set_player_leds(ctlr, 0, 0xF >> (4 - input_num));
-	if (ret)
-		hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret);
-	mutex_unlock(&ctlr->output_mutex);
-
-	/* configure the player LEDs */
-	for (i = 0; i < JC_NUM_LEDS; i++) {
-		name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s",
-				      d_name,
-				      "green",
-				      joycon_player_led_names[i]);
-		if (!name)
-			return -ENOMEM;
-
-		led = &ctlr->leds[i];
-		led->name = name;
-		led->brightness = ((i + 1) <= input_num) ? 1 : 0;
-		led->max_brightness = 1;
-		led->brightness_set_blocking =
-					joycon_player_led_brightness_set;
-		led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
-
-		ret = devm_led_classdev_register(&hdev->dev, led);
-		if (ret) {
-			hid_err(hdev, "Failed registering %s LED\n", led->name);
-			return ret;
-		}
-	}
-
-	if (++input_num > 4)
-		input_num = 1;
-	mutex_unlock(&joycon_input_num_mutex);
-
-	/* configure the home LED */
-	if (jc_type_has_right(ctlr)) {
-		name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s",
-				      d_name,
-				      "blue",
-				      LED_FUNCTION_PLAYER5);
-		if (!name)
-			return -ENOMEM;
-
-		led = &ctlr->home_led;
-		led->name = name;
-		led->brightness = 0;
-		led->max_brightness = 0xF;
-		led->brightness_set_blocking = joycon_home_led_brightness_set;
-		led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
-		ret = devm_led_classdev_register(&hdev->dev, led);
-		if (ret) {
-			hid_err(hdev, "Failed registering home led\n");
-			return ret;
-		}
-		/* Set the home LED to 0 as default state */
-		ret = joycon_home_led_brightness_set(led, 0);
-		if (ret) {
-			hid_err(hdev, "Failed to set home LED dflt; ret=%d\n",
-									ret);
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-static int joycon_battery_get_property(struct power_supply *supply,
-				       enum power_supply_property prop,
-				       union power_supply_propval *val)
-{
-	struct joycon_ctlr *ctlr = power_supply_get_drvdata(supply);
-	unsigned long flags;
-	int ret = 0;
-	u8 capacity;
-	bool charging;
-	bool powered;
-
-	spin_lock_irqsave(&ctlr->lock, flags);
-	capacity = ctlr->battery_capacity;
-	charging = ctlr->battery_charging;
-	powered = ctlr->host_powered;
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	switch (prop) {
-	case POWER_SUPPLY_PROP_PRESENT:
-		val->intval = 1;
-		break;
-	case POWER_SUPPLY_PROP_SCOPE:
-		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
-		break;
-	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
-		val->intval = capacity;
-		break;
-	case POWER_SUPPLY_PROP_STATUS:
-		if (charging)
-			val->intval = POWER_SUPPLY_STATUS_CHARGING;
-		else if (capacity == POWER_SUPPLY_CAPACITY_LEVEL_FULL &&
-			 powered)
-			val->intval = POWER_SUPPLY_STATUS_FULL;
-		else
-			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-	return ret;
-}
-
-static enum power_supply_property joycon_battery_props[] = {
-	POWER_SUPPLY_PROP_PRESENT,
-	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-	POWER_SUPPLY_PROP_SCOPE,
-	POWER_SUPPLY_PROP_STATUS,
-};
-
-static int joycon_power_supply_create(struct joycon_ctlr *ctlr)
-{
-	struct hid_device *hdev = ctlr->hdev;
-	struct power_supply_config supply_config = { .drv_data = ctlr, };
-	const char * const name_fmt = "nintendo_switch_controller_battery_%s";
-	int ret = 0;
-
-	/* Set initially to unknown before receiving first input report */
-	ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
-
-	/* Configure the battery's description */
-	ctlr->battery_desc.properties = joycon_battery_props;
-	ctlr->battery_desc.num_properties =
-					ARRAY_SIZE(joycon_battery_props);
-	ctlr->battery_desc.get_property = joycon_battery_get_property;
-	ctlr->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
-	ctlr->battery_desc.use_for_apm = 0;
-	ctlr->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
-						 name_fmt,
-						 dev_name(&hdev->dev));
-	if (!ctlr->battery_desc.name)
-		return -ENOMEM;
-
-	ctlr->battery = devm_power_supply_register(&hdev->dev,
-						   &ctlr->battery_desc,
-						   &supply_config);
-	if (IS_ERR(ctlr->battery)) {
-		ret = PTR_ERR(ctlr->battery);
-		hid_err(hdev, "Failed to register battery; ret=%d\n", ret);
-		return ret;
-	}
-
-	return power_supply_powers(ctlr->battery, &hdev->dev);
-}
-
-static int joycon_read_info(struct joycon_ctlr *ctlr)
-{
-	int ret;
-	int i;
-	int j;
-	struct joycon_subcmd_request req = { 0 };
-	struct joycon_input_report *report;
-
-	req.subcmd_id = JC_SUBCMD_REQ_DEV_INFO;
-	ret = joycon_send_subcmd(ctlr, &req, 0, HZ);
-	if (ret) {
-		hid_err(ctlr->hdev, "Failed to get joycon info; ret=%d\n", ret);
-		return ret;
-	}
-
-	report = (struct joycon_input_report *)ctlr->input_buf;
-
-	for (i = 4, j = 0; j < 6; i++, j++)
-		ctlr->mac_addr[j] = report->subcmd_reply.data[i];
-
-	ctlr->mac_addr_str = devm_kasprintf(&ctlr->hdev->dev, GFP_KERNEL,
-					    "%02X:%02X:%02X:%02X:%02X:%02X",
-					    ctlr->mac_addr[0],
-					    ctlr->mac_addr[1],
-					    ctlr->mac_addr[2],
-					    ctlr->mac_addr[3],
-					    ctlr->mac_addr[4],
-					    ctlr->mac_addr[5]);
-	if (!ctlr->mac_addr_str)
-		return -ENOMEM;
-	hid_info(ctlr->hdev, "controller MAC = %s\n", ctlr->mac_addr_str);
-
-	/* Retrieve the type so we can distinguish for charging grip */
-	ctlr->ctlr_type = report->subcmd_reply.data[2];
-
-	return 0;
-}
-
-/* Common handler for parsing inputs */
-static int joycon_ctlr_read_handler(struct joycon_ctlr *ctlr, u8 *data,
-							      int size)
-{
-	if (data[0] == JC_INPUT_SUBCMD_REPLY || data[0] == JC_INPUT_IMU_DATA ||
-	    data[0] == JC_INPUT_MCU_DATA) {
-		if (size >= 12) /* make sure it contains the input report */
-			joycon_parse_report(ctlr,
-					    (struct joycon_input_report *)data);
-	}
-
-	return 0;
-}
-
-static int joycon_ctlr_handle_event(struct joycon_ctlr *ctlr, u8 *data,
-							      int size)
-{
-	int ret = 0;
-	bool match = false;
-	struct joycon_input_report *report;
-
-	if (unlikely(mutex_is_locked(&ctlr->output_mutex)) &&
-	    ctlr->msg_type != JOYCON_MSG_TYPE_NONE) {
-		switch (ctlr->msg_type) {
-		case JOYCON_MSG_TYPE_USB:
-			if (size < 2)
-				break;
-			if (data[0] == JC_INPUT_USB_RESPONSE &&
-			    data[1] == ctlr->usb_ack_match)
-				match = true;
-			break;
-		case JOYCON_MSG_TYPE_SUBCMD:
-			if (size < sizeof(struct joycon_input_report) ||
-			    data[0] != JC_INPUT_SUBCMD_REPLY)
-				break;
-			report = (struct joycon_input_report *)data;
-			if (report->subcmd_reply.id == ctlr->subcmd_ack_match)
-				match = true;
-			break;
-		default:
-			break;
-		}
-
-		if (match) {
-			memcpy(ctlr->input_buf, data,
-			       min(size, (int)JC_MAX_RESP_SIZE));
-			ctlr->msg_type = JOYCON_MSG_TYPE_NONE;
-			ctlr->received_resp = true;
-			wake_up(&ctlr->wait);
-
-			/* This message has been handled */
-			return 1;
-		}
-	}
-
-	if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ)
-		ret = joycon_ctlr_read_handler(ctlr, data, size);
-
-	return ret;
-}
 
 static int nintendo_hid_event(struct hid_device *hdev,
 			      struct hid_report *report, u8 *raw_data, int size)
 {
-	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
+#ifdef CONFIG_HID_NINTENDO_SWITCH
+	enum nintendo_driver *driver = hid_get_drvdata(hdev);
 
-	if (size < 1)
-		return -EINVAL;
-
-	return joycon_ctlr_handle_event(ctlr, raw_data, size);
+	if (*driver == NINTENDO_SWITCH)
+		return switch_hid_event(hdev, report, raw_data, size);
+#endif
+	unreachable();
 }
 
 static int nintendo_hid_probe(struct hid_device *hdev,
-			    const struct hid_device_id *id)
+			      const struct hid_device_id *id)
 {
-	int ret;
-	struct joycon_ctlr *ctlr;
-
-	hid_dbg(hdev, "probe - start\n");
-
-	ctlr = devm_kzalloc(&hdev->dev, sizeof(*ctlr), GFP_KERNEL);
-	if (!ctlr) {
-		ret = -ENOMEM;
-		goto err;
-	}
-
-	ctlr->hdev = hdev;
-	ctlr->ctlr_state = JOYCON_CTLR_STATE_INIT;
-	ctlr->rumble_queue_head = JC_RUMBLE_QUEUE_SIZE - 1;
-	ctlr->rumble_queue_tail = 0;
-	hid_set_drvdata(hdev, ctlr);
-	mutex_init(&ctlr->output_mutex);
-	init_waitqueue_head(&ctlr->wait);
-	spin_lock_init(&ctlr->lock);
-	ctlr->rumble_queue = alloc_workqueue("hid-nintendo-rumble_wq",
-					     WQ_FREEZABLE | WQ_MEM_RECLAIM, 0);
-	INIT_WORK(&ctlr->rumble_worker, joycon_rumble_worker);
-
-	ret = hid_parse(hdev);
-	if (ret) {
-		hid_err(hdev, "HID parse failed\n");
-		goto err_wq;
-	}
-
-	/*
-	 * Patch the hw version of pro controller/joycons, so applications can
-	 * distinguish between the default HID mappings and the mappings defined
-	 * by the Linux game controller spec. This is important for the SDL2
-	 * library, which has a game controller database, which uses device ids
-	 * in combination with version as a key.
-	 */
-	hdev->version |= 0x8000;
-
-	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
-	if (ret) {
-		hid_err(hdev, "HW start failed\n");
-		goto err_wq;
-	}
-
-	ret = hid_hw_open(hdev);
-	if (ret) {
-		hid_err(hdev, "cannot start hardware I/O\n");
-		goto err_stop;
-	}
-
-	hid_device_io_start(hdev);
-
-	/* Initialize the controller */
-	mutex_lock(&ctlr->output_mutex);
-	/* if handshake command fails, assume ble pro controller */
-	if ((jc_type_is_procon(ctlr) || jc_type_is_chrggrip(ctlr)) &&
-	    !joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ)) {
-		hid_dbg(hdev, "detected USB controller\n");
-		/* set baudrate for improved latency */
-		ret = joycon_send_usb(ctlr, JC_USB_CMD_BAUDRATE_3M, HZ);
-		if (ret) {
-			hid_err(hdev, "Failed to set baudrate; ret=%d\n", ret);
-			goto err_mutex;
-		}
-		/* handshake */
-		ret = joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ);
-		if (ret) {
-			hid_err(hdev, "Failed handshake; ret=%d\n", ret);
-			goto err_mutex;
-		}
-		/*
-		 * Set no timeout (to keep controller in USB mode).
-		 * This doesn't send a response, so ignore the timeout.
-		 */
-		joycon_send_usb(ctlr, JC_USB_CMD_NO_TIMEOUT, HZ/10);
-	} else if (jc_type_is_chrggrip(ctlr)) {
-		hid_err(hdev, "Failed charging grip handshake\n");
-		ret = -ETIMEDOUT;
-		goto err_mutex;
-	}
-
-	/* get controller calibration data, and parse it */
-	ret = joycon_request_calibration(ctlr);
-	if (ret) {
-		/*
-		 * We can function with default calibration, but it may be
-		 * inaccurate. Provide a warning, and continue on.
-		 */
-		hid_warn(hdev, "Analog stick positions may be inaccurate\n");
-	}
-
-	/* get IMU calibration data, and parse it */
-	ret = joycon_request_imu_calibration(ctlr);
-	if (ret) {
-		/*
-		 * We can function with default calibration, but it may be
-		 * inaccurate. Provide a warning, and continue on.
-		 */
-		hid_warn(hdev, "Unable to read IMU calibration data\n");
-	}
-
-	/* Set the reporting mode to 0x30, which is the full report mode */
-	ret = joycon_set_report_mode(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to set report mode; ret=%d\n", ret);
-		goto err_mutex;
-	}
-
-	/* Enable rumble */
-	ret = joycon_enable_rumble(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to enable rumble; ret=%d\n", ret);
-		goto err_mutex;
-	}
-
-	/* Enable the IMU */
-	ret = joycon_enable_imu(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to enable the IMU; ret=%d\n", ret);
-		goto err_mutex;
-	}
-
-	ret = joycon_read_info(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to retrieve controller info; ret=%d\n",
-			ret);
-		goto err_mutex;
-	}
-
-	mutex_unlock(&ctlr->output_mutex);
-
-	/* Initialize the leds */
-	ret = joycon_leds_create(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to create leds; ret=%d\n", ret);
-		goto err_close;
-	}
-
-	/* Initialize the battery power supply */
-	ret = joycon_power_supply_create(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to create power_supply; ret=%d\n", ret);
-		goto err_close;
-	}
-
-	ret = joycon_input_create(ctlr);
-	if (ret) {
-		hid_err(hdev, "Failed to create input device; ret=%d\n", ret);
-		goto err_close;
-	}
-
-	ctlr->ctlr_state = JOYCON_CTLR_STATE_READ;
+	int ret = 0;
 
-	hid_dbg(hdev, "probe - success\n");
-	return 0;
+#ifdef CONFIG_HID_NINTENDO_SWITCH
+	ret = switch_hid_probe(hdev, id);
+#endif
 
-err_mutex:
-	mutex_unlock(&ctlr->output_mutex);
-err_close:
-	hid_hw_close(hdev);
-err_stop:
-	hid_hw_stop(hdev);
-err_wq:
-	destroy_workqueue(ctlr->rumble_queue);
-err:
-	hid_err(hdev, "probe - fail = %d\n", ret);
 	return ret;
 }
 
 static void nintendo_hid_remove(struct hid_device *hdev)
 {
-	struct joycon_ctlr *ctlr = hid_get_drvdata(hdev);
-	unsigned long flags;
+#ifdef CONFIG_HID_NINTENDO_SWITCH
+	enum nintendo_driver *driver = hid_get_drvdata(hdev);
 
-	hid_dbg(hdev, "remove\n");
-
-	/* Prevent further attempts at sending subcommands. */
-	spin_lock_irqsave(&ctlr->lock, flags);
-	ctlr->ctlr_state = JOYCON_CTLR_STATE_REMOVED;
-	spin_unlock_irqrestore(&ctlr->lock, flags);
-
-	destroy_workqueue(ctlr->rumble_queue);
-
-	hid_hw_close(hdev);
-	hid_hw_stop(hdev);
+	if (*driver == NINTENDO_SWITCH)
+		switch_hid_remove(hdev);
+#endif
 }
 
 static const struct hid_device_id nintendo_hid_devices[] = {
+#ifdef CONFIG_HID_NINTENDO_SWITCH
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
 			 USB_DEVICE_ID_NINTENDO_PROCON) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
@@ -2300,6 +59,8 @@ static const struct hid_device_id nintendo_hid_devices[] = {
 			 USB_DEVICE_ID_NINTENDO_JOYCONL) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
 			 USB_DEVICE_ID_NINTENDO_JOYCONR) },
+#endif
+
 	{ }
 };
 MODULE_DEVICE_TABLE(hid, nintendo_hid_devices);
@@ -2314,6 +75,6 @@ static struct hid_driver nintendo_hid_driver = {
 module_hid_driver(nintendo_hid_driver);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Daniel J. Ogorchock <djogorchock@gmail.com>");
-MODULE_DESCRIPTION("Driver for Nintendo Switch Controllers");
+MODULE_AUTHOR("Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>");
+MODULE_DESCRIPTION("Driver for Nintendo controllers");
 
diff --git a/drivers/hid/hid-nintendo.h b/drivers/hid/hid-nintendo.h
new file mode 100644
index 000000000000..56bb7e800cc2
--- /dev/null
+++ b/drivers/hid/hid-nintendo.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/kernel.h>
+#include <linux/hid.h>
+
+/* Every HID drvdata supported by this driver MUST start with this
+ * enum, so that dispatch can work properly. */
+enum nintendo_driver {
+	NINTENDO_SWITCH,
+};
+
+int switch_hid_event(struct hid_device *hdev,
+		     struct hid_report *report, u8 *raw_data, int size);
+int switch_hid_probe(struct hid_device *hdev,
+		     const struct hid_device_id *id);
+void switch_hid_remove(struct hid_device *hdev);
-- 
2.33.1


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

* [PATCH v5 2/5] HID: nintendo: drc: add support for the Wii U gamepad
  2021-10-27 10:10     ` [PATCH v5 " Emmanuel Gil Peyrot
  2021-10-27 10:10       ` [PATCH v5 1/5] HID: nintendo: split switch support into its own file Emmanuel Gil Peyrot
@ 2021-10-27 10:10       ` Emmanuel Gil Peyrot
  2021-10-27 10:10       ` [PATCH v5 3/5] HID: nintendo: drc: implement touch reports Emmanuel Gil Peyrot
                         ` (2 subsequent siblings)
  4 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-27 10:10 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Ash Logan, linux-input, Jonathan Neuschäfer,
	Barnabás Pőcze, Benjamin Tissoires, linux-kernel,
	Daniel J . Ogorchock, Emmanuel Gil Peyrot

From: Ash Logan <ash@heyquark.com>

This driver is for the DRC (gamepad) of the Wii U when wirelessly
connected to the DRH of the console, an internal chip exposing it as a
USB device.

This first patch exposes the buttons and sticks of this device, so that
it can act as a plain game controller.

The report format has been described by the libdrc folks at:
https://libdrc.org/docs/re/sc-input.html

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/Kconfig             |   8 +
 drivers/hid/Makefile            |   1 +
 drivers/hid/hid-ids.h           |   1 +
 drivers/hid/hid-nintendo-wiiu.c | 267 ++++++++++++++++++++++++++++++++
 drivers/hid/hid-nintendo.c      |  21 ++-
 drivers/hid/hid-nintendo.h      |   6 +
 drivers/hid/hid-quirks.c        |   3 +
 7 files changed, 304 insertions(+), 3 deletions(-)
 create mode 100644 drivers/hid/hid-nintendo-wiiu.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 17ed06f5a23c..6bcc0d181cc4 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -749,6 +749,14 @@ config NINTENDO_FF
 	controller. For the pro controller, both rumble motors can be controlled
 	individually.
 
+config HID_NINTENDO_WIIU
+	tristate "Nintendo Wii U gamepad (DRC) over internal DRH"
+	default n
+	depends on HID_NINTENDO
+	help
+	  Support for the Wii U gamepad, when connected with the Wii U’s
+	  internal DRH chip.
+
 config HID_NINTENDO_SWITCH
 	tristate "Nintendo Wii U gamepad (DRC) over internal DRH"
 	default y
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index a0a9ee182ef2..f9851a7acea9 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_HID_MICROSOFT)	+= hid-microsoft.o
 obj-$(CONFIG_HID_MONTEREY)	+= hid-monterey.o
 obj-$(CONFIG_HID_MULTITOUCH)	+= hid-multitouch.o
 obj-$(CONFIG_HID_NINTENDO)	+= hid-nintendo.o
+obj-$(CONFIG_HID_NINTENDO_WIIU)	+= hid-nintendo-wiiu.o
 obj-$(CONFIG_HID_NINTENDO_SWITCH)	+= hid-nintendo-switch.o
 obj-$(CONFIG_HID_NTI)			+= hid-nti.o
 obj-$(CONFIG_HID_NTRIG)		+= hid-ntrig.o
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index dc8f2f402464..30ce4287c48b 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -917,6 +917,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_WIIU_DRH	0x0341
 #define USB_DEVICE_ID_NINTENDO_JOYCONL	0x2006
 #define USB_DEVICE_ID_NINTENDO_JOYCONR	0x2007
 #define USB_DEVICE_ID_NINTENDO_PROCON	0x2009
diff --git a/drivers/hid/hid-nintendo-wiiu.c b/drivers/hid/hid-nintendo-wiiu.c
new file mode 100644
index 000000000000..0b9df62587ed
--- /dev/null
+++ b/drivers/hid/hid-nintendo-wiiu.c
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID driver for Nintendo Wii U gamepad (DRC), connected via console-internal DRH
+ *
+ * Copyright (C) 2021 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+ * Copyright (C) 2019 Ash Logan <ash@heyquark.com>
+ * Copyright (C) 2013 Mema Hacking
+ *
+ * Based on the excellent work at http://libdrc.org/docs/re/sc-input.html and
+ * https://bitbucket.org/memahaxx/libdrc/src/master/src/input-receiver.cpp .
+ * libdrc code is licensed under BSD 2-Clause.
+ * Driver based on hid-udraw-ps3.c.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include "hid-ids.h"
+#include "hid-nintendo.h"
+
+#define DEVICE_NAME	"Nintendo Wii U gamepad (DRC)"
+
+/* Button and stick constants */
+#define VOLUME_MIN	0
+#define VOLUME_MAX	255
+#define NUM_STICK_AXES	4
+#define STICK_MIN	900
+#define STICK_MAX	3200
+
+#define BUTTON_SYNC	BIT(0)
+#define BUTTON_HOME	BIT(1)
+#define BUTTON_MINUS	BIT(2)
+#define BUTTON_PLUS	BIT(3)
+#define BUTTON_R	BIT(4)
+#define BUTTON_L	BIT(5)
+#define BUTTON_ZR	BIT(6)
+#define BUTTON_ZL	BIT(7)
+#define BUTTON_DOWN	BIT(8)
+#define BUTTON_UP	BIT(9)
+#define BUTTON_RIGHT	BIT(10)
+#define BUTTON_LEFT	BIT(11)
+#define BUTTON_Y	BIT(12)
+#define BUTTON_X	BIT(13)
+#define BUTTON_B	BIT(14)
+#define BUTTON_A	BIT(15)
+
+#define BUTTON_TV	BIT(21)
+#define BUTTON_R3	BIT(22)
+#define BUTTON_L3	BIT(23)
+
+#define BUTTON_POWER	BIT(25)
+
+/*
+ * The device is setup with multiple input devices:
+ * - A joypad with the buttons and sticks.
+ */
+
+struct drc {
+	enum nintendo_driver driver;
+	struct hid_device *hdev;
+	struct input_dev *joy_input_dev;
+};
+
+/*
+ * The format of this report has been reversed by the libdrc project, the
+ * documentation can be found here:
+ * https://libdrc.org/docs/re/sc-input.html
+ *
+ * We receive this report from USB, but it is actually formed on the DRC, the
+ * DRH only retransmits it over USB.
+ */
+int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
+		   u8 *data, int len)
+{
+	struct drc *drc = hid_get_drvdata(hdev);
+	int i;
+	u32 buttons;
+
+	if (len != 128)
+		return -EINVAL;
+
+	buttons = (data[4] << 24) | (data[80] << 16) | (data[2] << 8) | data[3];
+	/* joypad */
+	input_report_key(drc->joy_input_dev, BTN_DPAD_RIGHT, buttons & BUTTON_RIGHT);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_DOWN, buttons & BUTTON_DOWN);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_LEFT, buttons & BUTTON_LEFT);
+	input_report_key(drc->joy_input_dev, BTN_DPAD_UP, buttons & BUTTON_UP);
+
+	input_report_key(drc->joy_input_dev, BTN_EAST, buttons & BUTTON_A);
+	input_report_key(drc->joy_input_dev, BTN_SOUTH, buttons & BUTTON_B);
+	input_report_key(drc->joy_input_dev, BTN_NORTH, buttons & BUTTON_X);
+	input_report_key(drc->joy_input_dev, BTN_WEST, buttons & BUTTON_Y);
+
+	input_report_key(drc->joy_input_dev, BTN_TL, buttons & BUTTON_L);
+	input_report_key(drc->joy_input_dev, BTN_TL2, buttons & BUTTON_ZL);
+	input_report_key(drc->joy_input_dev, BTN_TR, buttons & BUTTON_R);
+	input_report_key(drc->joy_input_dev, BTN_TR2, buttons & BUTTON_ZR);
+
+	input_report_key(drc->joy_input_dev, BTN_Z, buttons & BUTTON_TV);
+	input_report_key(drc->joy_input_dev, BTN_THUMBL, buttons & BUTTON_L3);
+	input_report_key(drc->joy_input_dev, BTN_THUMBR, buttons & BUTTON_R3);
+
+	input_report_key(drc->joy_input_dev, BTN_SELECT, buttons & BUTTON_MINUS);
+	input_report_key(drc->joy_input_dev, BTN_START, buttons & BUTTON_PLUS);
+	input_report_key(drc->joy_input_dev, BTN_MODE, buttons & BUTTON_HOME);
+
+	input_report_key(drc->joy_input_dev, BTN_DEAD, buttons & BUTTON_POWER);
+
+	for (i = 0; i < NUM_STICK_AXES; i++) {
+		s16 val = (data[7 + 2*i] << 8) | data[6 + 2*i];
+
+		val = clamp(val, (s16)STICK_MIN, (s16)STICK_MAX);
+
+		switch (i) {
+		case 0:
+			input_report_abs(drc->joy_input_dev, ABS_X, val);
+			break;
+		case 1:
+			input_report_abs(drc->joy_input_dev, ABS_Y, val);
+			break;
+		case 2:
+			input_report_abs(drc->joy_input_dev, ABS_RX, val);
+			break;
+		case 3:
+			input_report_abs(drc->joy_input_dev, ABS_RY, val);
+			break;
+		default:
+			break;
+		}
+	}
+
+	input_report_abs(drc->joy_input_dev, ABS_VOLUME, data[14]);
+	input_sync(drc->joy_input_dev);
+
+	/* let hidraw and hiddev handle the report */
+	return 0;
+}
+
+static int drc_open(struct input_dev *dev)
+{
+	struct drc *drc = input_get_drvdata(dev);
+
+	return hid_hw_open(drc->hdev);
+}
+
+static void drc_close(struct input_dev *dev)
+{
+	struct drc *drc = input_get_drvdata(dev);
+
+	hid_hw_close(drc->hdev);
+}
+
+static struct input_dev *allocate_and_setup(struct hid_device *hdev,
+					    const char *name)
+{
+	struct input_dev *input_dev;
+
+	input_dev = devm_input_allocate_device(&hdev->dev);
+	if (!input_dev)
+		return NULL;
+
+	input_dev->name = name;
+	input_dev->phys = hdev->phys;
+	input_dev->dev.parent = &hdev->dev;
+	input_dev->open = drc_open;
+	input_dev->close = drc_close;
+	input_dev->uniq = hdev->uniq;
+	input_dev->id.bustype = hdev->bus;
+	input_dev->id.vendor  = hdev->vendor;
+	input_dev->id.product = hdev->product;
+	input_dev->id.version = hdev->version;
+	input_set_drvdata(input_dev, hid_get_drvdata(hdev));
+
+	return input_dev;
+}
+
+static bool drc_setup_joypad(struct drc *drc,
+			     struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " buttons and sticks");
+	if (!input_dev)
+		return false;
+
+	drc->joy_input_dev = input_dev;
+
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_RIGHT);
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_DOWN);
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_LEFT);
+	input_set_capability(input_dev, EV_KEY, BTN_DPAD_UP);
+	input_set_capability(input_dev, EV_KEY, BTN_EAST);
+	input_set_capability(input_dev, EV_KEY, BTN_SOUTH);
+	input_set_capability(input_dev, EV_KEY, BTN_NORTH);
+	input_set_capability(input_dev, EV_KEY, BTN_WEST);
+	input_set_capability(input_dev, EV_KEY, BTN_TL);
+	input_set_capability(input_dev, EV_KEY, BTN_TL2);
+	input_set_capability(input_dev, EV_KEY, BTN_TR);
+	input_set_capability(input_dev, EV_KEY, BTN_TR2);
+	input_set_capability(input_dev, EV_KEY, BTN_THUMBL);
+	input_set_capability(input_dev, EV_KEY, BTN_THUMBR);
+	input_set_capability(input_dev, EV_KEY, BTN_SELECT);
+	input_set_capability(input_dev, EV_KEY, BTN_START);
+	input_set_capability(input_dev, EV_KEY, BTN_MODE);
+
+	/*
+	 * These two buttons are actually TV Control and Power.
+	 *
+	 * TV Control draws a line at the bottom of the DRC’s screen saying to
+	 * go into System Settings (on the original proprietary OS), while
+	 * Power will shutdown the DRC when held for four seconds, but those
+	 * two are still normal buttons otherwise.
+	 */
+	input_set_capability(input_dev, EV_KEY, BTN_Z);
+	input_set_capability(input_dev, EV_KEY, BTN_DEAD);
+
+	input_set_abs_params(input_dev, ABS_X, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RX, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RY, STICK_MIN, STICK_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_VOLUME, VOLUME_MIN, VOLUME_MAX, 0, 0);
+
+	return true;
+}
+
+int wiiu_hid_probe(struct hid_device *hdev,
+		   const struct hid_device_id *id)
+{
+	struct drc *drc;
+	int ret;
+
+	drc = devm_kzalloc(&hdev->dev, sizeof(struct drc), GFP_KERNEL);
+	if (!drc)
+		return -ENOMEM;
+
+	drc->driver = NINTENDO_WIIU;
+	drc->hdev = hdev;
+
+	hid_set_drvdata(hdev, drc);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "parse failed\n");
+		return ret;
+	}
+
+	if (!drc_setup_joypad(drc, hdev)) {
+		hid_err(hdev, "could not allocate interface\n");
+		return -ENOMEM;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER);
+	if (ret) {
+		hid_err(hdev, "hw start failed\n");
+		return ret;
+	}
+
+	ret = input_register_device(drc->joy_input_dev);
+	if (ret) {
+		hid_err(hdev, "failed to register interface\n");
+		return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index f16d8d94b1da..0b0588f5bb8a 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -16,9 +16,15 @@
 static int nintendo_hid_event(struct hid_device *hdev,
 			      struct hid_report *report, u8 *raw_data, int size)
 {
-#ifdef CONFIG_HID_NINTENDO_SWITCH
+#if defined(CONFIG_HID_NINTENDO_SWITCH) || defined(CONFIG_HID_NINTENDO_WIIU)
 	enum nintendo_driver *driver = hid_get_drvdata(hdev);
+#endif
 
+#ifdef CONFIG_HID_NINTENDO_WIIU
+	if (*driver == NINTENDO_WIIU)
+		return wiiu_hid_event(hdev, report, raw_data, size);
+#endif
+#ifdef CONFIG_HID_NINTENDO_SWITCH
 	if (*driver == NINTENDO_SWITCH)
 		return switch_hid_event(hdev, report, raw_data, size);
 #endif
@@ -30,10 +36,14 @@ static int nintendo_hid_probe(struct hid_device *hdev,
 {
 	int ret = 0;
 
+#ifdef CONFIG_HID_NINTENDO_WIIU
+	if (id->product == USB_DEVICE_ID_NINTENDO_WIIU_DRH)
+		ret = wiiu_hid_probe(hdev, id);
+#endif
 #ifdef CONFIG_HID_NINTENDO_SWITCH
-	ret = switch_hid_probe(hdev, id);
+	if (id->product != USB_DEVICE_ID_NINTENDO_WIIU_DRH)
+		ret = switch_hid_probe(hdev, id);
 #endif
-
 	return ret;
 }
 
@@ -48,6 +58,11 @@ static void nintendo_hid_remove(struct hid_device *hdev)
 }
 
 static const struct hid_device_id nintendo_hid_devices[] = {
+#ifdef CONFIG_HID_NINTENDO_WIIU
+	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
+			 USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
+#endif
+
 #ifdef CONFIG_HID_NINTENDO_SWITCH
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
 			 USB_DEVICE_ID_NINTENDO_PROCON) },
diff --git a/drivers/hid/hid-nintendo.h b/drivers/hid/hid-nintendo.h
index 56bb7e800cc2..eba300ee039e 100644
--- a/drivers/hid/hid-nintendo.h
+++ b/drivers/hid/hid-nintendo.h
@@ -6,9 +6,15 @@
 /* Every HID drvdata supported by this driver MUST start with this
  * enum, so that dispatch can work properly. */
 enum nintendo_driver {
+	NINTENDO_WIIU,
 	NINTENDO_SWITCH,
 };
 
+int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
+		   u8 *data, int len);
+int wiiu_hid_probe(struct hid_device *hdev,
+		   const struct hid_device_id *id);
+
 int switch_hid_event(struct hid_device *hdev,
 		     struct hid_report *report, u8 *raw_data, int size);
 int switch_hid_probe(struct hid_device *hdev,
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 2e104682c22b..6b13edfa195d 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -517,6 +517,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
 #endif
+#if IS_ENABLED(CONFIG_HID_WIIU_DRC)
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIU_DRH) },
+#endif
 #if IS_ENABLED(CONFIG_HID_NTI)
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NTI, USB_DEVICE_ID_USB_SUN) },
 #endif
-- 
2.33.1


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

* [PATCH v5 3/5] HID: nintendo: drc: implement touch reports
  2021-10-27 10:10     ` [PATCH v5 " Emmanuel Gil Peyrot
  2021-10-27 10:10       ` [PATCH v5 1/5] HID: nintendo: split switch support into its own file Emmanuel Gil Peyrot
  2021-10-27 10:10       ` [PATCH v5 2/5] HID: nintendo: drc: add support for the Wii U gamepad Emmanuel Gil Peyrot
@ 2021-10-27 10:10       ` Emmanuel Gil Peyrot
  2021-10-27 10:10       ` [PATCH v5 4/5] HID: nintendo: drc: add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
  2021-10-27 10:10       ` [PATCH v5 5/5] HID: nintendo: drc: add battery reporting Emmanuel Gil Peyrot
  4 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-27 10:10 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock

There is an inaccessible border on each side, 100 units on the left and
right sides, and 200 units at the top and bottom.  The Y axis is
inverted too, these are the two main quirks of this touch panel.

I’ve been testing with weston-simple-touch mostly, but it also with the
rest of Weston and it aligns perfectly without the need of any
additional calibration.

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-nintendo-wiiu.c | 86 +++++++++++++++++++++++++++++++--
 1 file changed, 81 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/hid-nintendo-wiiu.c b/drivers/hid/hid-nintendo-wiiu.c
index 0b9df62587ed..144316d324cb 100644
--- a/drivers/hid/hid-nintendo-wiiu.c
+++ b/drivers/hid/hid-nintendo-wiiu.c
@@ -52,15 +52,29 @@
 
 #define BUTTON_POWER	BIT(25)
 
+/* Touch constants */
+/* Resolution in pixels */
+#define RES_X		854
+#define RES_Y		480
+/* Display/touch size in mm */
+#define WIDTH		138
+#define HEIGHT		79
+#define NUM_TOUCH_POINTS 10
+#define MAX_TOUCH_RES	(1 << 12)
+#define TOUCH_BORDER_X	100
+#define TOUCH_BORDER_Y	200
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
+ * - The touch area which works as a touchscreen.
  */
 
 struct drc {
 	enum nintendo_driver driver;
 	struct hid_device *hdev;
 	struct input_dev *joy_input_dev;
+	struct input_dev *touch_input_dev;
 };
 
 /*
@@ -75,7 +89,7 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 		   u8 *data, int len)
 {
 	struct drc *drc = hid_get_drvdata(hdev);
-	int i;
+	int i, x, y, pressure, base;
 	u32 buttons;
 
 	if (len != 128)
@@ -134,6 +148,42 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 	input_report_abs(drc->joy_input_dev, ABS_VOLUME, data[14]);
 	input_sync(drc->joy_input_dev);
 
+	/* touch */
+	/*
+	 * Average touch points for improved accuracy.  Sadly these are always
+	 * reported extremely close from each other…  Even when the user
+	 * pressed two (or more) different points, all ten values will be
+	 * approximately in the middle of the pressure points.
+	 */
+	x = y = 0;
+	for (i = 0; i < NUM_TOUCH_POINTS; i++) {
+		base = 36 + 4 * i;
+
+		x += ((data[base + 1] & 0xF) << 8) | data[base];
+		y += ((data[base + 3] & 0xF) << 8) | data[base + 2];
+	}
+	x /= NUM_TOUCH_POINTS;
+	y /= NUM_TOUCH_POINTS;
+
+	/* Pressure reporting isn’t properly understood, so we don’t report it yet. */
+	pressure = 0;
+	pressure |= ((data[37] >> 4) & 7) << 0;
+	pressure |= ((data[39] >> 4) & 7) << 3;
+	pressure |= ((data[41] >> 4) & 7) << 6;
+	pressure |= ((data[43] >> 4) & 7) << 9;
+
+	if (pressure != 0) {
+		input_report_key(drc->touch_input_dev, BTN_TOUCH, 1);
+		input_report_key(drc->touch_input_dev, BTN_TOOL_FINGER, 1);
+
+		input_report_abs(drc->touch_input_dev, ABS_X, x);
+		input_report_abs(drc->touch_input_dev, ABS_Y, MAX_TOUCH_RES - y);
+	} else {
+		input_report_key(drc->touch_input_dev, BTN_TOUCH, 0);
+		input_report_key(drc->touch_input_dev, BTN_TOOL_FINGER, 0);
+	}
+	input_sync(drc->touch_input_dev);
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -225,6 +275,30 @@ static bool drc_setup_joypad(struct drc *drc,
 	return true;
 }
 
+static bool drc_setup_touch(struct drc *drc,
+			    struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " touchscreen");
+	if (!input_dev)
+		return false;
+
+	drc->touch_input_dev = input_dev;
+
+	set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+	input_set_abs_params(input_dev, ABS_X, TOUCH_BORDER_X, MAX_TOUCH_RES - TOUCH_BORDER_X, 20, 0);
+	input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH);
+	input_set_abs_params(input_dev, ABS_Y, TOUCH_BORDER_Y, MAX_TOUCH_RES - TOUCH_BORDER_Y, 20, 0);
+	input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT);
+
+	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+	input_set_capability(input_dev, EV_KEY, BTN_TOOL_FINGER);
+
+	return true;
+}
+
 int wiiu_hid_probe(struct hid_device *hdev,
 		   const struct hid_device_id *id)
 {
@@ -246,8 +320,9 @@ int wiiu_hid_probe(struct hid_device *hdev,
 		return ret;
 	}
 
-	if (!drc_setup_joypad(drc, hdev)) {
-		hid_err(hdev, "could not allocate interface\n");
+	if (!drc_setup_joypad(drc, hdev) ||
+	    !drc_setup_touch(drc, hdev)) {
+		hid_err(hdev, "could not allocate interfaces\n");
 		return -ENOMEM;
 	}
 
@@ -257,9 +332,10 @@ int wiiu_hid_probe(struct hid_device *hdev,
 		return ret;
 	}
 
-	ret = input_register_device(drc->joy_input_dev);
+	ret = input_register_device(drc->joy_input_dev) ||
+	      input_register_device(drc->touch_input_dev);
 	if (ret) {
-		hid_err(hdev, "failed to register interface\n");
+		hid_err(hdev, "failed to register interfaces\n");
 		return ret;
 	}
 
-- 
2.33.1


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

* [PATCH v5 4/5] HID: nintendo: drc: add accelerometer, gyroscope and magnetometer readings
  2021-10-27 10:10     ` [PATCH v5 " Emmanuel Gil Peyrot
                         ` (2 preceding siblings ...)
  2021-10-27 10:10       ` [PATCH v5 3/5] HID: nintendo: drc: implement touch reports Emmanuel Gil Peyrot
@ 2021-10-27 10:10       ` Emmanuel Gil Peyrot
  2021-10-27 10:10       ` [PATCH v5 5/5] HID: nintendo: drc: add battery reporting Emmanuel Gil Peyrot
  4 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-27 10:10 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock

These are mostly untested so far, because I have no idea which userland
to test against, but evtest at least seems to give sensible values.

The magnetometer doesn’t have dedicated INPUT_PROP_ACCELEROMETER
buttons, so I used three clearly invalid absolute values, in the hope
that someone will fix that eventually.  Another solution might be to go
for the iio subsystem instead, but then it wouldn’t be tied to the HID
any longer and I would feel uneasy about that.  Especially because
multiple such gamepads could be connected to a single computer.

Signed-off-by: Ash Logan <ash@heyquark.com>
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-nintendo-wiiu.c | 77 +++++++++++++++++++++++++++++++--
 1 file changed, 74 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/hid-nintendo-wiiu.c b/drivers/hid/hid-nintendo-wiiu.c
index 144316d324cb..813abb104275 100644
--- a/drivers/hid/hid-nintendo-wiiu.c
+++ b/drivers/hid/hid-nintendo-wiiu.c
@@ -64,10 +64,19 @@
 #define TOUCH_BORDER_X	100
 #define TOUCH_BORDER_Y	200
 
+/* Accelerometer, gyroscope and magnetometer constants */
+#define ACCEL_MIN	-(1 << 15)
+#define ACCEL_MAX	((1 << 15) - 1)
+#define GYRO_MIN	-(1 << 23)
+#define GYRO_MAX	((1 << 23) - 1)
+#define MAGNET_MIN	-(1 << 15)
+#define MAGNET_MAX	((1 << 15) - 1)
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
  * - The touch area which works as a touchscreen.
+ * - An accelerometer + gyroscope + magnetometer device.
  */
 
 struct drc {
@@ -75,6 +84,7 @@ struct drc {
 	struct hid_device *hdev;
 	struct input_dev *joy_input_dev;
 	struct input_dev *touch_input_dev;
+	struct input_dev *accel_input_dev;
 };
 
 /*
@@ -89,7 +99,7 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 		   u8 *data, int len)
 {
 	struct drc *drc = hid_get_drvdata(hdev);
-	int i, x, y, pressure, base;
+	int i, x, y, z, pressure, base;
 	u32 buttons;
 
 	if (len != 128)
@@ -184,6 +194,31 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 	}
 	input_sync(drc->touch_input_dev);
 
+	/* accelerometer */
+	x = (data[16] << 8) | data[15];
+	y = (data[18] << 8) | data[17];
+	z = (data[20] << 8) | data[19];
+	input_report_abs(drc->accel_input_dev, ABS_X, (int16_t)x);
+	input_report_abs(drc->accel_input_dev, ABS_Y, (int16_t)y);
+	input_report_abs(drc->accel_input_dev, ABS_Z, (int16_t)z);
+
+	/* gyroscope */
+	x = (data[23] << 24) | (data[22] << 16) | (data[21] << 8);
+	y = (data[26] << 24) | (data[25] << 16) | (data[24] << 8);
+	z = (data[29] << 24) | (data[28] << 16) | (data[27] << 8);
+	input_report_abs(drc->accel_input_dev, ABS_RX, x >> 8);
+	input_report_abs(drc->accel_input_dev, ABS_RY, y >> 8);
+	input_report_abs(drc->accel_input_dev, ABS_RZ, z >> 8);
+
+	/* magnetometer */
+	x = (data[31] << 8) | data[30];
+	y = (data[33] << 8) | data[32];
+	z = (data[35] << 8) | data[34];
+	input_report_abs(drc->accel_input_dev, ABS_THROTTLE, (int16_t)x);
+	input_report_abs(drc->accel_input_dev, ABS_RUDDER, (int16_t)y);
+	input_report_abs(drc->accel_input_dev, ABS_WHEEL, (int16_t)z);
+	input_sync(drc->accel_input_dev);
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -299,6 +334,40 @@ static bool drc_setup_touch(struct drc *drc,
 	return true;
 }
 
+static bool drc_setup_accel(struct drc *drc,
+			    struct hid_device *hdev)
+{
+	struct input_dev *input_dev;
+
+	input_dev = allocate_and_setup(hdev, DEVICE_NAME " accelerometer, gyroscope and magnetometer");
+	if (!input_dev)
+		return false;
+
+	drc->accel_input_dev = input_dev;
+
+	set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit);
+
+	/* 1G accel is reported as about -7900 */
+	input_set_abs_params(input_dev, ABS_X, ACCEL_MIN, ACCEL_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, ACCEL_MIN, ACCEL_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_Z, ACCEL_MIN, ACCEL_MAX, 0, 0);
+
+	/* gyroscope */
+	input_set_abs_params(input_dev, ABS_RX, GYRO_MIN, GYRO_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RY, GYRO_MIN, GYRO_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RZ, GYRO_MIN, GYRO_MAX, 0, 0);
+
+	/* magnetometer */
+	/* TODO: Figure out which ABS_* would make more sense to expose, or
+	 * maybe go for the iio subsystem?
+	 */
+	input_set_abs_params(input_dev, ABS_THROTTLE, MAGNET_MIN, MAGNET_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_RUDDER, MAGNET_MIN, MAGNET_MAX, 0, 0);
+	input_set_abs_params(input_dev, ABS_WHEEL, MAGNET_MIN, MAGNET_MAX, 0, 0);
+
+	return true;
+}
+
 int wiiu_hid_probe(struct hid_device *hdev,
 		   const struct hid_device_id *id)
 {
@@ -321,7 +390,8 @@ int wiiu_hid_probe(struct hid_device *hdev,
 	}
 
 	if (!drc_setup_joypad(drc, hdev) ||
-	    !drc_setup_touch(drc, hdev)) {
+	    !drc_setup_touch(drc, hdev) ||
+	    !drc_setup_accel(drc, hdev)) {
 		hid_err(hdev, "could not allocate interfaces\n");
 		return -ENOMEM;
 	}
@@ -333,7 +403,8 @@ int wiiu_hid_probe(struct hid_device *hdev,
 	}
 
 	ret = input_register_device(drc->joy_input_dev) ||
-	      input_register_device(drc->touch_input_dev);
+	      input_register_device(drc->touch_input_dev) ||
+	      input_register_device(drc->accel_input_dev);
 	if (ret) {
 		hid_err(hdev, "failed to register interfaces\n");
 		return ret;
-- 
2.33.1


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

* [PATCH v5 5/5] HID: nintendo: drc: add battery reporting
  2021-10-27 10:10     ` [PATCH v5 " Emmanuel Gil Peyrot
                         ` (3 preceding siblings ...)
  2021-10-27 10:10       ` [PATCH v5 4/5] HID: nintendo: drc: add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
@ 2021-10-27 10:10       ` Emmanuel Gil Peyrot
  4 siblings, 0 replies; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-10-27 10:10 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock

On my DRC the values only go between 142 (battery LED blinking red
before shutdown) and 178 (charge LED stopping), it seems to be the same
on other units according to other testers.  This might be the raw
voltage value as reported by an ADC, so adding a linear interpolation
between two common battery voltage values.

A spinlock is used to avoid the battery level and status from being
reported unsynchronised.

Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
---
 drivers/hid/hid-nintendo-wiiu.c | 136 ++++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/drivers/hid/hid-nintendo-wiiu.c b/drivers/hid/hid-nintendo-wiiu.c
index 813abb104275..b18fa403eb42 100644
--- a/drivers/hid/hid-nintendo-wiiu.c
+++ b/drivers/hid/hid-nintendo-wiiu.c
@@ -17,6 +17,11 @@
 #include <linux/input.h>
 #include <linux/minmax.h>
 #include <linux/module.h>
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+#include <linux/fixp-arith.h>
+#include <linux/power_supply.h>
+#include <linux/spinlock.h>
+#endif
 #include "hid-ids.h"
 #include "hid-nintendo.h"
 
@@ -72,6 +77,13 @@
 #define MAGNET_MIN	-(1 << 15)
 #define MAGNET_MAX	((1 << 15) - 1)
 
+/* ADC constants for the battery */
+#define BATTERY_CHARGING_BIT	BIT(6)
+#define BATTERY_MIN	142
+#define BATTERY_MAX	178
+#define VOLTAGE_MIN	3270000
+#define VOLTAGE_MAX	4100000
+
 /*
  * The device is setup with multiple input devices:
  * - A joypad with the buttons and sticks.
@@ -85,6 +97,14 @@ struct drc {
 	struct input_dev *joy_input_dev;
 	struct input_dev *touch_input_dev;
 	struct input_dev *accel_input_dev;
+
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	struct power_supply *battery;
+	struct power_supply_desc battery_desc;
+	spinlock_t battery_lock;
+	u8 battery_energy;
+	int battery_status;
+#endif
 };
 
 /*
@@ -101,6 +121,9 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 	struct drc *drc = hid_get_drvdata(hdev);
 	int i, x, y, z, pressure, base;
 	u32 buttons;
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	unsigned long flags;
+#endif
 
 	if (len != 128)
 		return -EINVAL;
@@ -219,6 +242,19 @@ int wiiu_hid_event(struct hid_device *hdev, struct hid_report *report,
 	input_report_abs(drc->accel_input_dev, ABS_WHEEL, (int16_t)z);
 	input_sync(drc->accel_input_dev);
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	/* battery */
+	spin_lock_irqsave(&drc->battery_lock, flags);
+	drc->battery_energy = data[5];
+	if (drc->battery_energy == BATTERY_MAX)
+		drc->battery_status = POWER_SUPPLY_STATUS_FULL;
+	else if (data[4] & BATTERY_CHARGING_BIT)
+		drc->battery_status = POWER_SUPPLY_STATUS_CHARGING;
+	else
+		drc->battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+	spin_unlock_irqrestore(&drc->battery_lock, flags);
+#endif
+
 	/* let hidraw and hiddev handle the report */
 	return 0;
 }
@@ -368,6 +404,98 @@ static bool drc_setup_accel(struct drc *drc,
 	return true;
 }
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+static enum power_supply_property drc_battery_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_SCOPE,
+};
+
+static int drc_battery_get_property(struct power_supply *psy,
+				    enum power_supply_property psp,
+				    union power_supply_propval *val)
+{
+	struct drc *drc = power_supply_get_drvdata(psy);
+	unsigned long flags;
+	int ret = 0;
+	u8 battery_energy;
+	int battery_status;
+
+	spin_lock_irqsave(&drc->battery_lock, flags);
+	battery_energy = drc->battery_energy;
+	battery_status = drc->battery_status;
+	spin_unlock_irqrestore(&drc->battery_lock, flags);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = battery_status;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		val->intval = VOLTAGE_MAX;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		val->intval = VOLTAGE_MIN;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = fixp_linear_interpolate(BATTERY_MIN, VOLTAGE_MIN,
+						      BATTERY_MAX, VOLTAGE_MAX,
+						      battery_energy);
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = fixp_linear_interpolate(BATTERY_MIN, 0,
+						      BATTERY_MAX, 100,
+						      battery_energy);
+		break;
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int drc_setup_battery(struct drc *drc,
+			     struct hid_device *hdev)
+{
+	struct power_supply_config psy_cfg = { .drv_data = drc, };
+	static atomic_t drc_num = ATOMIC_INIT(0);
+	int ret;
+
+	spin_lock_init(&drc->battery_lock);
+
+	drc->battery_desc.properties = drc_battery_props;
+	drc->battery_desc.num_properties = ARRAY_SIZE(drc_battery_props);
+	drc->battery_desc.get_property = drc_battery_get_property;
+	drc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+	drc->battery_desc.use_for_apm = 0;
+
+	drc->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
+						"wiiu-drc-%i-battery", atomic_fetch_inc(&drc_num));
+	if (!drc->battery_desc.name)
+		return -ENOMEM;
+
+	drc->battery = devm_power_supply_register(&hdev->dev, &drc->battery_desc, &psy_cfg);
+	if (IS_ERR(drc->battery)) {
+		ret = PTR_ERR(drc->battery);
+		hid_err(hdev, "Unable to register battery device\n");
+		return ret;
+	}
+
+	power_supply_powers(drc->battery, &hdev->dev);
+
+	return 0;
+}
+#endif
+
 int wiiu_hid_probe(struct hid_device *hdev,
 		   const struct hid_device_id *id)
 {
@@ -396,6 +524,14 @@ int wiiu_hid_probe(struct hid_device *hdev,
 		return -ENOMEM;
 	}
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+	ret = drc_setup_battery(drc, hdev);
+	if (ret) {
+		hid_err(hdev, "could not allocate battery interface\n");
+		return ret;
+	}
+#endif
+
 	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER);
 	if (ret) {
 		hid_err(hdev, "hw start failed\n");
-- 
2.33.1


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

* Re: [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad
  2021-10-19  9:30         ` Jiri Kosina
  2021-10-19  9:36           ` Emmanuel Gil Peyrot
@ 2021-11-04 11:21           ` Emmanuel Gil Peyrot
  2021-11-05 17:27             ` François-Xavier Carton
  1 sibling, 1 reply; 41+ messages in thread
From: Emmanuel Gil Peyrot @ 2021-11-04 11:21 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Emmanuel Gil Peyrot, linux-input, Ash Logan,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J. Ogorchock

[-- Attachment #1: Type: text/plain, Size: 778 bytes --]

On Tue, Oct 19, 2021 at 11:30:06AM +0200, Jiri Kosina wrote:
> On Tue, 19 Oct 2021, Emmanuel Gil Peyrot wrote:
[…]
> > Another driver I’d like to submit eventually is the GameCube Controller 
> > Adapter for Wii U, which does exactly what its name says, but being an 
> > external USB adapter it also works on any USB computer; would it make 
> > sense to develop it alongside the current driver, just because it is 
> > sold by the same company?
> 
> We generally group the support for HID devices in drivers based on the 
> producing company, with a few exceptions where it doesn't make sense.

Speaking of which, would you want me to also merge hid-wiimote into
hid-nintendo?  Or is there a reason it is separate besides legacy?

-- 
Emmanuel Gil Peyrot

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad
  2021-11-04 11:21           ` Emmanuel Gil Peyrot
@ 2021-11-05 17:27             ` François-Xavier Carton
  0 siblings, 0 replies; 41+ messages in thread
From: François-Xavier Carton @ 2021-11-05 17:27 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot
  Cc: Jiri Kosina, linux-input, Ash Logan, Jonathan Neuschäfer,
	Barnabás Pőcze, Benjamin Tissoires, linux-kernel,
	Daniel J. Ogorchock

On Thu, Nov 04, 2021 at 12:21:37PM +0100, Emmanuel Gil Peyrot wrote:
> On Tue, Oct 19, 2021 at 11:30:06AM +0200, Jiri Kosina wrote:
> > On Tue, 19 Oct 2021, Emmanuel Gil Peyrot wrote:
> […]
> > > Another driver I’d like to submit eventually is the GameCube Controller 
> > > Adapter for Wii U, which does exactly what its name says, but being an 
> > > external USB adapter it also works on any USB computer; would it make 
> > > sense to develop it alongside the current driver, just because it is 
> > > sold by the same company?
> > 
> > We generally group the support for HID devices in drivers based on the 
> > producing company, with a few exceptions where it doesn't make sense.
> 
> Speaking of which, would you want me to also merge hid-wiimote into
> hid-nintendo?  Or is there a reason it is separate besides legacy?
> 

Would naming the drivers with a "nintendo-" prefix while keeping them
separate be an acceptable solution? Since these drivers share no common
code, merging them will result in a big driver with different parts for
unrelated hardware (save for the maker company), which doesn't seem
right.

For the gamecube adapter driver, I'd prefer to keep it separate; but
I'll integrate it to hid-nintendo as Emmanuel did for the wii-u if
that's the preferred option.

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

* Re: [PATCH v4 2/5] HID: nintendo: drc: add support for the Wii U gamepad
  2021-10-19 11:04     ` [PATCH v4 2/5] HID: nintendo: drc: add support for the Wii U gamepad Emmanuel Gil Peyrot
@ 2021-11-05 20:55       ` kernel test robot
  0 siblings, 0 replies; 41+ messages in thread
From: kernel test robot @ 2021-11-05 20:55 UTC (permalink / raw)
  To: Emmanuel Gil Peyrot, Jiri Kosina
  Cc: llvm, kbuild-all, Ash Logan, linux-input,
	Jonathan Neuschäfer, Barnabás Pőcze,
	Benjamin Tissoires, linux-kernel, Daniel J . Ogorchock,
	Emmanuel Gil Peyrot

[-- Attachment #1: Type: text/plain, Size: 3019 bytes --]

Hi Emmanuel,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on hid/for-next]
[cannot apply to jikos-hid/for-next jikos-trivial/for-next v5.15 next-20211105]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Emmanuel-Gil-Peyrot/HID-nintendo-split-switch-support-into-its-own-file/20211019-200531
base:   https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git for-next
config: x86_64-randconfig-r036-20211027 (attached as .config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project 5db7568a6a1fcb408eb8988abdaff2a225a8eb72)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/8f5b7e20f3960b78503b3a6239e3238493029852
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Emmanuel-Gil-Peyrot/HID-nintendo-split-switch-support-into-its-own-file/20211019-200531
        git checkout 8f5b7e20f3960b78503b3a6239e3238493029852
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/hid/hid-nintendo.c:38:6: warning: variable 'ret' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
           if (id->product == USB_DEVICE_ID_NINTENDO_WIIU_DRH)
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/hid/hid-nintendo.c:45:9: note: uninitialized use occurs here
           return ret;
                  ^~~
   drivers/hid/hid-nintendo.c:38:2: note: remove the 'if' if its condition is always true
           if (id->product == USB_DEVICE_ID_NINTENDO_WIIU_DRH)
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/hid/hid-nintendo.c:35:9: note: initialize the variable 'ret' to silence this warning
           int ret;
                  ^
                   = 0
   1 warning generated.


vim +38 drivers/hid/hid-nintendo.c

    31	
    32	static int nintendo_hid_probe(struct hid_device *hdev,
    33				      const struct hid_device_id *id)
    34	{
    35		int ret;
    36	
    37	#ifdef CONFIG_HID_NINTENDO_WIIU
  > 38		if (id->product == USB_DEVICE_ID_NINTENDO_WIIU_DRH)
    39			ret = wiiu_hid_probe(hdev, id);
    40	#endif
    41	#ifdef CONFIG_HID_NINTENDO_SWITCH
    42		if (id->product != USB_DEVICE_ID_NINTENDO_WIIU_DRH)
    43			ret = switch_hid_probe(hdev, id);
    44	#endif
    45		return ret;
    46	}
    47	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 40110 bytes --]

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

end of thread, other threads:[~2021-11-05 20:56 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-02 23:28 [PATCH 0/4] RFC: HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
2021-05-02 23:28 ` [PATCH 1/4] HID: wiiu-drc: Add a driver for this gamepad Emmanuel Gil Peyrot
2021-05-05 22:33   ` Jonathan Neuschäfer
2021-05-06 10:07     ` Emmanuel Gil Peyrot
2021-05-06 10:29       ` Jonathan Neuschäfer
2021-05-06 11:53   ` Barnabás Pőcze
2021-05-02 23:28 ` [PATCH 2/4] HID: wiiu-drc: Implement touch reports Emmanuel Gil Peyrot
2021-05-05 22:43   ` Jonathan Neuschäfer
2021-05-06 10:20     ` Emmanuel Gil Peyrot
2021-05-02 23:28 ` [PATCH 3/4] HID: wiiu-drc: Add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
2021-05-02 23:28 ` [PATCH 4/4] HID: wiiu-drc: Add battery reporting Emmanuel Gil Peyrot
2021-05-06 11:45   ` Barnabás Pőcze
2021-05-19  8:59 ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
2021-05-19  8:59   ` [PATCH v3 1/4] HID: wiiu-drc: Add a driver for this gamepad Emmanuel Gil Peyrot
2021-05-19  8:59   ` [PATCH v3 2/4] HID: wiiu-drc: Implement touch reports Emmanuel Gil Peyrot
2021-05-19  8:59   ` [PATCH v3 3/4] HID: wiiu-drc: Add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
2021-05-19  8:59   ` [PATCH v3 4/4] HID: wiiu-drc: Add battery reporting Emmanuel Gil Peyrot
2021-09-21 15:08   ` [PATCH v3 0/4] HID: wiiu-drc: Add a driver for the Wii U gamepad Emmanuel Gil Peyrot
2021-10-19  9:14     ` Jiri Kosina
2021-10-19  9:27       ` Emmanuel Gil Peyrot
2021-10-19  9:30         ` Jiri Kosina
2021-10-19  9:36           ` Emmanuel Gil Peyrot
2021-11-04 11:21           ` Emmanuel Gil Peyrot
2021-11-05 17:27             ` François-Xavier Carton
2021-10-19 23:59         ` François-Xavier Carton
2021-10-20  6:24           ` Emmanuel Gil Peyrot
2021-10-19 11:04   ` [PATCH v4 0/5] HID: nintendo: Add support " Emmanuel Gil Peyrot
2021-10-19 11:04     ` [PATCH v4 1/5] HID: nintendo: split switch support into its own file Emmanuel Gil Peyrot
2021-10-22  8:32       ` kernel test robot
2021-10-19 11:04     ` [PATCH v4 2/5] HID: nintendo: drc: add support for the Wii U gamepad Emmanuel Gil Peyrot
2021-11-05 20:55       ` kernel test robot
2021-10-19 11:04     ` [PATCH v4 3/5] HID: nintendo: drc: implement touch reports Emmanuel Gil Peyrot
2021-10-19 11:04     ` [PATCH v4 4/5] HID: nintendo: drc: add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
2021-10-19 11:04     ` [PATCH v4 5/5] HID: nintendo: drc: add battery reporting Emmanuel Gil Peyrot
2021-10-27  8:10     ` [PATCH v4 0/5] HID: nintendo: Add support for the Wii U gamepad Jiri Kosina
2021-10-27 10:10     ` [PATCH v5 " Emmanuel Gil Peyrot
2021-10-27 10:10       ` [PATCH v5 1/5] HID: nintendo: split switch support into its own file Emmanuel Gil Peyrot
2021-10-27 10:10       ` [PATCH v5 2/5] HID: nintendo: drc: add support for the Wii U gamepad Emmanuel Gil Peyrot
2021-10-27 10:10       ` [PATCH v5 3/5] HID: nintendo: drc: implement touch reports Emmanuel Gil Peyrot
2021-10-27 10:10       ` [PATCH v5 4/5] HID: nintendo: drc: add accelerometer, gyroscope and magnetometer readings Emmanuel Gil Peyrot
2021-10-27 10:10       ` [PATCH v5 5/5] HID: nintendo: drc: add battery reporting Emmanuel Gil Peyrot

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