SDL: Added support for the HORIPAD FPS for Nintendo Switch

From d6a8b43cf79a47d0d6a45da236e1caf235bb6033 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 5 Aug 2022 10:37:38 -0700
Subject: [PATCH] Added support for the HORIPAD FPS for Nintendo Switch

---
 src/hidapi/libusb/hid.c        | 38 +++++++++++++++++++++++++++++++++-
 src/joystick/controller_type.c |  1 +
 2 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c
index 5a3b22a470a..d2c407c965b 100644
--- a/src/hidapi/libusb/hid.c
+++ b/src/hidapi/libusb/hid.c
@@ -529,6 +529,22 @@ static struct usb_string_cache_entry *usb_string_cache_insert()
 	return new_entry;
 }
 
+static int usb_string_can_cache(uint16_t vid, uint16_t pid)
+{
+	if (!vid || !pid) {
+		/* We can't cache these, they aren't unique */
+		return 0;
+	}
+
+	if (vid == 0x0f0d && pid == 0x00dc) {
+		/* HORI reuses this VID/PID for many different products */
+		return 0;
+	}
+
+	/* We can cache these strings */
+	return 1;
+}
+
 static const struct usb_string_cache_entry *usb_string_cache_find(struct libusb_device_descriptor *desc, struct libusb_device_handle *handle)
 {
 	struct usb_string_cache_entry *entry = NULL;
@@ -684,6 +700,8 @@ static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_de
 
 static int should_enumerate_interface(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc)
 {
+	//printf("Checking interface 0x%x %d/%d/%d/%d\n", vendor_id, intf_desc->bInterfaceNumber, intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol);
+
 	if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID)
 		return 1;
 
@@ -766,7 +784,7 @@ struct hid_device_info  HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
 										get_usb_string(handle, desc.iSerialNumber);
 
 								/* Manufacturer and Product strings */
-								if (dev_vid && dev_pid) {
+								if (usb_string_can_cache(dev_vid, dev_pid)) {
 									string_cache = usb_string_cache_find(&desc, handle);
 									if (string_cache) {
 										if (string_cache->vendor) {
@@ -1067,6 +1085,19 @@ static int SDLCALL read_thread(void *param)
 	return 0;
 }
 
+static void init_xbox360(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, struct libusb_config_descriptor *conf_desc)
+{
+	if (idVendor == 0x0f0d && idProduct == 0x00dc) {
+		unsigned char data[20];
+
+		/* The HORIPAD FPS for Nintendo Switch requires this to enable input reports.
+		   This VID/PID is also shared with other HORI controllers, but they all seem
+		   to be fine with this as well.
+		 */
+		libusb_control_transfer(device_handle, 0xC1, 0x01, 0x100, 0x0, data, sizeof(data), 100);
+	}
+}
+
 static void init_xboxone(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, struct libusb_config_descriptor *conf_desc)
 {
 	static const int VENDOR_MICROSOFT = 0x045e;
@@ -1186,6 +1217,11 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive)
 							break;
 						}
 
+						/* Initialize XBox 360 controllers */
+						if (is_xbox360(desc.idVendor, intf_desc)) {
+							init_xbox360(dev->device_handle, desc.idVendor, desc.idProduct, conf_desc);
+						}
+
 						/* Initialize XBox One controllers */
 						if (is_xboxone(desc.idVendor, intf_desc)) {
 							init_xboxone(dev->device_handle, desc.idVendor, desc.idProduct, conf_desc);
diff --git a/src/joystick/controller_type.c b/src/joystick/controller_type.c
index bf4c8ee7bb1..1c66ac2e76d 100644
--- a/src/joystick/controller_type.c
+++ b/src/joystick/controller_type.c
@@ -546,6 +546,7 @@ static const ControllerDescription_t arrControllers[] = {
 	// - NSW-108, classic GameCube controller
 	// - NSW-244, Fighting Commander arcade pad
 	// - NSW-278, Hori Pad Mini gamepad
+	// - NSW-326, HORIPAD FPS for Nintendo Switch
 	//
 	// The first two, at least, shouldn't have their buttons remapped, and since we
 	// can't tell which model we're actually using, we won't do any button remapping