diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c index d0e1b5c112..0c4fc660ba 100644 --- a/src/hidapi/libusb/hid.c +++ b/src/hidapi/libusb/hid.c @@ -1324,82 +1324,6 @@ static void init_xboxone(libusb_device_handle *device_handle, unsigned short idV } } -static bool is_ns2(unsigned short idVendor, unsigned short idProduct) -{ - if (idVendor == 0x057e) { - if (idProduct == 0x2069) { - return true; - } - if (idProduct == 0x2073) { - return true; - } - } - return false; -} - -static void init_ns2(libusb_device_handle *device_handle, const struct libusb_config_descriptor *conf_desc) -{ - int j, k, l, res; - - for (j = 0; j < conf_desc->bNumInterfaces; j++) { - const struct libusb_interface *intf = &conf_desc->interface[j]; - for (k = 0; k < intf->num_altsetting; k++) { - const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k]; - if (intf_desc->bInterfaceNumber == 1) { - uint8_t endpoint = 0; - for (l = 0; l < intf_desc->bNumEndpoints; l++) { - const struct libusb_endpoint_descriptor* ep = &intf_desc->endpoint[l]; - if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK && (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) { - endpoint = ep->bEndpointAddress; - break; - } - } - - if (endpoint) { - res = libusb_claim_interface(device_handle, intf_desc->bInterfaceNumber); - if (res < 0) { - LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); - continue; - } - - const unsigned char DEFAULT_REPORT_DATA[] = { - 0x03, 0x91, 0x00, 0x0d, 0x00, 0x08, - 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF - }; - const unsigned char SET_LED_DATA[] = { - 0x09, 0x91, 0x00, 0x07, 0x00, 0x08, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - int transferred; - res = libusb_bulk_transfer(device_handle, - endpoint, - (unsigned char*)DEFAULT_REPORT_DATA, - sizeof(DEFAULT_REPORT_DATA), - &transferred, - 1000); - if (res < 0) { - LOG("can't set report data: %d\n", res); - } - - res = libusb_bulk_transfer(device_handle, - endpoint, - (unsigned char*)SET_LED_DATA, - sizeof(SET_LED_DATA), - &transferred, - 1000); - if (res < 0) { - LOG("can't set LED data: %d\n", res); - } - - libusb_release_interface(device_handle, intf_desc->bInterfaceNumber); - return; - } - } - } - } -} - static void calculate_device_quirks(hid_device *dev, unsigned short idVendor, unsigned short idProduct) { static const int VENDOR_SONY = 0x054c; @@ -1461,11 +1385,6 @@ static int hidapi_initialize_device(hid_device *dev, const struct libusb_interfa init_xboxone(dev->device_handle, desc.idVendor, desc.idProduct, conf_desc); } - /* Initialize Nintendo Switch 2 controllers */ - if (is_ns2(desc.idVendor, desc.idProduct)) { - init_ns2(dev->device_handle, conf_desc); - } - /* Store off the string descriptor indexes */ dev->manufacturer_index = desc.iManufacturer; dev->product_index = desc.iProduct; diff --git a/src/joystick/hidapi/SDL_hidapi_switch2.c b/src/joystick/hidapi/SDL_hidapi_switch2.c index 8e0a23a1a0..3655cc02b1 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch2.c +++ b/src/joystick/hidapi/SDL_hidapi_switch2.c @@ -26,13 +26,21 @@ #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" -#include "SDL_hidapi_nintendo.h" #ifdef SDL_JOYSTICK_HIDAPI_SWITCH2 +typedef struct +{ + SDL_LibUSBContext *libusb; + libusb_device_handle *device_handle; + bool interface_claimed; + Uint8 interface_number; + Uint8 bulk_endpoint; +} SDL_DriverSwitch2_Context; + static void HIDAPI_DriverSwitch2_RegisterHints(SDL_HintCallback callback, void *userdata) { SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, callback, userdata); @@ -51,29 +59,138 @@ static bool HIDAPI_DriverSwitch2_IsEnabled(void) static bool HIDAPI_DriverSwitch2_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) { if (vendor_id == USB_VENDOR_NINTENDO) { - switch (product_id) { - case USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER: - case USB_PRODUCT_NINTENDO_SWITCH2_PRO: + switch (product_id) { + case USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER: + case USB_PRODUCT_NINTENDO_SWITCH2_PRO: return true; - } + } } return false; } +static bool HIDAPI_DriverSwitch2_InitBluetooth(SDL_HIDAPI_Device *device) +{ + // FIXME: Need to add Bluetooth support + return SDL_SetError("Nintendo Switch2 controllers not supported over Bluetooth"); +} + +static bool FindBulkOutEndpoint(SDL_LibUSBContext *libusb, libusb_device_handle *handle, Uint8 *bInterfaceNumber, Uint8 *bEndpointAddress) +{ + struct libusb_config_descriptor *config; + if (libusb->get_config_descriptor(libusb->get_device(handle), 0, &config) != 0) { + return false; + } + + for (int i = 0; i < config->bNumInterfaces; i++) { + const struct libusb_interface *iface = &config->interface[i]; + for (int j = 0; j < iface->num_altsetting; j++) { + const struct libusb_interface_descriptor *altsetting = &iface->altsetting[j]; + if (altsetting->bInterfaceNumber == 1) { + for (int k = 0; k < altsetting->bNumEndpoints; k++) { + const struct libusb_endpoint_descriptor *ep = &altsetting->endpoint[k]; + if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK && (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) { + + *bInterfaceNumber = altsetting->bInterfaceNumber; + *bEndpointAddress = ep->bEndpointAddress; + libusb->free_config_descriptor(config); + return true; + } + } + } + } + } + libusb->free_config_descriptor(config); + return false; +} + +static bool HIDAPI_DriverSwitch2_InitUSB(SDL_HIDAPI_Device *device) +{ + SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context; + + if (!SDL_InitLibUSB(&ctx->libusb)) { + return false; + } + + ctx->device_handle = (libusb_device_handle *)SDL_GetPointerProperty(SDL_hid_get_properties(device->dev), SDL_PROP_HIDAPI_LIBUSB_DEVICE_HANDLE_POINTER, NULL); + if (!ctx->device_handle) { + return SDL_SetError("Couldn't get libusb device handle"); + } + + if (!FindBulkOutEndpoint(ctx->libusb, ctx->device_handle, &ctx->interface_number, &ctx->bulk_endpoint)) { + return SDL_SetError("Couldn't find bulk endpoint"); + } + + int res = ctx->libusb->claim_interface(ctx->device_handle, ctx->interface_number); + if (res < 0) { + return SDL_SetError("Couldn't claim interface %d: %d\n", ctx->interface_number, res); + } + ctx->interface_claimed = true; + + const unsigned char DEFAULT_REPORT_DATA[] = { + 0x03, 0x91, 0x00, 0x0d, 0x00, 0x08, + 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + const unsigned char SET_LED_DATA[] = { + 0x09, 0x91, 0x00, 0x07, 0x00, 0x08, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + int transferred; + res = ctx->libusb->bulk_transfer(ctx->device_handle, + ctx->bulk_endpoint, + (unsigned char *)DEFAULT_REPORT_DATA, + sizeof(DEFAULT_REPORT_DATA), + &transferred, + 1000); + if (res < 0) { + return SDL_SetError("Couldn't set report data: %d\n", res); + } + + res = ctx->libusb->bulk_transfer(ctx->device_handle, + ctx->bulk_endpoint, + (unsigned char *)SET_LED_DATA, + sizeof(SET_LED_DATA), + &transferred, + 1000); + if (res < 0) { + return SDL_SetError("Couldn't set LED data: %d\n", res); + } + + return true; +} + static bool HIDAPI_DriverSwitch2_InitDevice(SDL_HIDAPI_Device *device) { - // Sometimes the device handle isn't available during enumeration so we don't get the device name, so set it explicitly - switch (device->product_id) { - case USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER: - HIDAPI_SetDeviceName(device, "Nintendo GameCube Controller"); - break; - case USB_PRODUCT_NINTENDO_SWITCH2_PRO: - HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller"); - break; - default: - break; - } + SDL_DriverSwitch2_Context *ctx; + + ctx = (SDL_DriverSwitch2_Context *)SDL_calloc(1, sizeof(*ctx)); + if (!ctx) { + return false; + } + device->context = ctx; + + if (device->is_bluetooth) { + if (!HIDAPI_DriverSwitch2_InitBluetooth(device)) { + return false; + } + } else { + if (!HIDAPI_DriverSwitch2_InitUSB(device)) { + return false; + } + } + + // Sometimes the device handle isn't available during enumeration so we don't get the device name, so set it explicitly + switch (device->product_id) { + case USB_PRODUCT_NINTENDO_SWITCH2_GAMECUBE_CONTROLLER: + HIDAPI_SetDeviceName(device, "Nintendo GameCube Controller"); + break; + case USB_PRODUCT_NINTENDO_SWITCH2_PRO: + HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller"); + break; + default: + break; + } return HIDAPI_JoystickConnected(device, NULL); } @@ -250,6 +367,18 @@ static void HIDAPI_DriverSwitch2_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Jo static void HIDAPI_DriverSwitch2_FreeDevice(SDL_HIDAPI_Device *device) { + SDL_DriverSwitch2_Context *ctx = (SDL_DriverSwitch2_Context *)device->context; + + if (ctx) { + if (ctx->interface_claimed) { + ctx->libusb->release_interface(ctx->device_handle, ctx->interface_number); + ctx->interface_claimed = false; + } + if (ctx->libusb) { + SDL_QuitLibUSB(); + ctx->libusb = NULL; + } + } } SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch2 = { diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h index 35ded59ba1..a8962468fb 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h +++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h @@ -35,7 +35,9 @@ #define SDL_JOYSTICK_HIDAPI_STEAM #define SDL_JOYSTICK_HIDAPI_STEAMDECK #define SDL_JOYSTICK_HIDAPI_SWITCH +#ifdef HAVE_LIBUSB #define SDL_JOYSTICK_HIDAPI_SWITCH2 +#endif #define SDL_JOYSTICK_HIDAPI_WII #define SDL_JOYSTICK_HIDAPI_XBOX360 #define SDL_JOYSTICK_HIDAPI_XBOXONE