From 9f444b398151e829a281c79bc3678db095131619 Mon Sep 17 00:00:00 2001 From: Sam Lantinga 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 e3d90453e6..13bfc4ed5e 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 6d94f77eb9..77272d3e61 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 b95fb89c4a..1bdc737377 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