From 9f444b398151e829a281c79bc3678db095131619 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 19 Nov 2025 16:11:18 -0800
Subject: [PATCH] Fixed initializing EVORETRO GameCube adapters
The HID device needs to be closed while enabling input reports over USB
---
src/hidapi/SDL_hidapi.c | 57 --------------
src/hidapi/SDL_hidapi_c.h | 9 ---
src/joystick/hidapi/SDL_hidapi_gamecube.c | 91 ++++++++++++++++++++---
3 files changed, 79 insertions(+), 78 deletions(-)
diff --git a/src/hidapi/SDL_hidapi.c b/src/hidapi/SDL_hidapi.c
index e3d90453e613f..13bfc4ed5ee74 100644
--- a/src/hidapi/SDL_hidapi.c
+++ b/src/hidapi/SDL_hidapi.c
@@ -1619,60 +1619,3 @@ void SDL_hid_ble_scan(bool active)
hid_ble_scan(active);
#endif
}
-
-#ifdef HAVE_ENABLE_GAMECUBE_ADAPTORS
-// This is needed to enable input for Nyko and EVORETRO GameCube adaptors
-void SDL_EnableGameCubeAdaptors(void)
-{
-#ifdef HAVE_LIBUSB
- libusb_context *context = NULL;
- libusb_device **devs = NULL;
- libusb_device_handle *handle = NULL;
- struct libusb_device_descriptor desc;
- ssize_t i, num_devs;
- int kernel_detached = 0;
-
- if (!libusb_ctx) {
- return;
- }
-
- if (libusb_ctx->init(&context) == 0) {
- num_devs = libusb_ctx->get_device_list(context, &devs);
- for (i = 0; i < num_devs; ++i) {
- if (libusb_ctx->get_device_descriptor(devs[i], &desc) != 0) {
- continue;
- }
-
- if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) {
- continue;
- }
-
- if (libusb_ctx->open(devs[i], &handle) != 0) {
- continue;
- }
-
- if (libusb_ctx->kernel_driver_active(handle, 0)) {
- if (libusb_ctx->detach_kernel_driver(handle, 0) == 0) {
- kernel_detached = 1;
- }
- }
-
- if (libusb_ctx->claim_interface(handle, 0) == 0) {
- libusb_ctx->control_transfer(handle, 0x21, 11, 0x0001, 0, NULL, 0, 1000);
- libusb_ctx->release_interface(handle, 0);
- }
-
- if (kernel_detached) {
- libusb_ctx->attach_kernel_driver(handle, 0);
- }
-
- libusb_ctx->close(handle);
- }
-
- libusb_ctx->free_device_list(devs, 1);
-
- libusb_ctx->exit(context);
- }
-#endif // HAVE_LIBUSB
-}
-#endif // HAVE_ENABLE_GAMECUBE_ADAPTORS
diff --git a/src/hidapi/SDL_hidapi_c.h b/src/hidapi/SDL_hidapi_c.h
index 6d94f77eb9526..77272d3e6125d 100644
--- a/src/hidapi/SDL_hidapi_c.h
+++ b/src/hidapi/SDL_hidapi_c.h
@@ -24,12 +24,3 @@
/* Return true if the HIDAPI should ignore a device during enumeration */
extern bool SDL_HIDAPI_ShouldIgnoreDevice(int bus_type, Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage);
-#ifdef SDL_JOYSTICK_HIDAPI
-#ifdef HAVE_LIBUSB
-#define HAVE_ENABLE_GAMECUBE_ADAPTORS
-#endif
-
-#ifdef HAVE_ENABLE_GAMECUBE_ADAPTORS
-extern void SDL_EnableGameCubeAdaptors(void);
-#endif
-#endif /* SDL_JOYSTICK_HIDAPI */
diff --git a/src/joystick/hidapi/SDL_hidapi_gamecube.c b/src/joystick/hidapi/SDL_hidapi_gamecube.c
index b95fb89c4ab4b..1bdc737377e52 100644
--- a/src/joystick/hidapi/SDL_hidapi_gamecube.c
+++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c
@@ -23,6 +23,7 @@
#ifdef SDL_JOYSTICK_HIDAPI
#include "../../SDL_hints_c.h"
+#include "../../misc/SDL_libusb.h"
#include "../SDL_sysjoystick.h"
#include "SDL_hidapijoystick_c.h"
#include "SDL_hidapi_rumble.h"
@@ -102,6 +103,83 @@ static void SDLCALL SDL_JoystickGameCubeRumbleBrakeHintChanged(void *userdata, c
}
}
+static bool HIDAPI_DriverGameCube_EnableAdapter(SDL_HIDAPI_Device *device)
+{
+#ifdef HAVE_LIBUSB
+ // Need to close the device while sending USB commands to it
+ SDL_hid_close(device->dev);
+
+ // This is needed to enable input for Nyko and EVORETRO GameCube adapters
+ SDL_LibUSBContext *libusb_ctx;
+ if (SDL_InitLibUSB(&libusb_ctx)) {
+ libusb_context *context = NULL;
+ libusb_device **devs = NULL;
+ libusb_device_handle *handle = NULL;
+ struct libusb_device_descriptor desc;
+ ssize_t i, num_devs;
+ bool kernel_detached = false;
+
+ if (libusb_ctx->init(&context) == 0) {
+ num_devs = libusb_ctx->get_device_list(context, &devs);
+ for (i = 0; i < num_devs; ++i) {
+ if (libusb_ctx->get_device_descriptor(devs[i], &desc) != 0) {
+ continue;
+ }
+
+ if (desc.idVendor != USB_VENDOR_NINTENDO ||
+ desc.idProduct != USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) {
+ continue;
+ }
+
+ if (libusb_ctx->open(devs[i], &handle) != 0) {
+ continue;
+ }
+
+ if (libusb_ctx->kernel_driver_active(handle, 0)) {
+ if (libusb_ctx->detach_kernel_driver(handle, 0) == 0) {
+ kernel_detached = true;
+ }
+ }
+
+ if (libusb_ctx->claim_interface(handle, 0) == 0) {
+ libusb_ctx->control_transfer(handle, 0x21, 11, 0x0001, 0, NULL, 0, 1000);
+ libusb_ctx->release_interface(handle, 0);
+ }
+
+ if (kernel_detached) {
+ libusb_ctx->attach_kernel_driver(handle, 0);
+ }
+
+ libusb_ctx->close(handle);
+ }
+
+ libusb_ctx->free_device_list(devs, 1);
+
+ libusb_ctx->exit(context);
+ }
+ SDL_QuitLibUSB();
+ }
+
+ // Reopen the device now that we're done
+ device->dev = SDL_hid_open_path(device->path);
+ if (!device->dev) {
+ return false;
+ }
+#endif // HAVE_LIBUSB
+
+ Uint8 initMagic = 0x13;
+ if (SDL_hid_write(device->dev, &initMagic, sizeof(initMagic)) != sizeof(initMagic)) {
+ SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+ "HIDAPI_DriverGameCube_InitDevice(): Couldn't initialize WUP-028");
+ return false;
+ }
+
+ // Wait for the adapter to initialize
+ SDL_Delay(10);
+
+ return true;
+}
+
static bool HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverGameCube_Context *ctx;
@@ -109,13 +187,8 @@ static bool HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device)
Uint8 *curSlot;
Uint8 i;
int size;
- Uint8 initMagic = 0x13;
Uint8 rumbleMagic = 0x11;
-#ifdef HAVE_ENABLE_GAMECUBE_ADAPTORS
- SDL_EnableGameCubeAdaptors();
-#endif
-
ctx = (SDL_DriverGameCube_Context *)SDL_calloc(1, sizeof(*ctx));
if (!ctx) {
return false;
@@ -132,16 +205,10 @@ static bool HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device)
ResetAxisRange(ctx, 0);
HIDAPI_JoystickConnected(device, &ctx->joysticks[0]);
} else {
- // This is all that's needed to initialize the device. Really!
- if (SDL_hid_write(device->dev, &initMagic, sizeof(initMagic)) != sizeof(initMagic)) {
- SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
- "HIDAPI_DriverGameCube_InitDevice(): Couldn't initialize WUP-028");
+ if (!HIDAPI_DriverGameCube_EnableAdapter(device)) {
return false;
}
- // Wait for the adapter to initialize
- SDL_Delay(10);
-
// Add all the applicable joysticks
while ((size = SDL_hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) {
#ifdef DEBUG_GAMECUBE_PROTOCOL