From 51fefd1c35746258ab427a18e731d15d9df7c339 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 23 Sep 2022 02:39:35 -0700 Subject: [PATCH] Added support for third party PS3 controllers to the HIDAPI driver --- src/joystick/hidapi/SDL_hidapi_ps3.c | 302 +++++++++++++++++++++ src/joystick/hidapi/SDL_hidapijoystick.c | 1 + src/joystick/hidapi/SDL_hidapijoystick_c.h | 1 + 3 files changed, 304 insertions(+) diff --git a/src/joystick/hidapi/SDL_hidapi_ps3.c b/src/joystick/hidapi/SDL_hidapi_ps3.c index 1b60c460f..a90d4a3bf 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps3.c +++ b/src/joystick/hidapi/SDL_hidapi_ps3.c @@ -592,6 +592,308 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS3 = HIDAPI_DriverPS3_FreeDevice, }; + +static SDL_bool +HIDAPI_DriverPS3ThirdParty_IsEnabled(void) +{ + return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS3, + SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, + SDL_HIDAPI_DEFAULT)); +} + +static SDL_bool +HIDAPI_DriverPS3ThirdParty_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) +{ + Uint8 data[USB_PACKET_LENGTH]; + int size; + + if (SONY_THIRDPARTY_VENDOR(vendor_id)) { + if (device) { + if ((size = ReadFeatureReport(device->dev, 0x03, data, sizeof(data))) == 8 && + data[2] == 0x26) { + /* Supported third party controller */ + return SDL_TRUE; + } else { + return SDL_FALSE; + } + } else { + /* Might be supported by this driver, enumerate and find out */ + return SDL_TRUE; + } + } + return SDL_FALSE; +} + +static SDL_bool +HIDAPI_DriverPS3ThirdParty_InitDevice(SDL_HIDAPI_Device *device) +{ + SDL_DriverPS3_Context *ctx; + + ctx = (SDL_DriverPS3_Context *)SDL_calloc(1, sizeof(*ctx)); + if (!ctx) { + SDL_OutOfMemory(); + return SDL_FALSE; + } + ctx->device = device; + + device->context = ctx; + + device->type = SDL_CONTROLLER_TYPE_PS3; + + return HIDAPI_JoystickConnected(device, NULL); +} + +static int +HIDAPI_DriverPS3ThirdParty_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) +{ + return -1; +} + +static void +HIDAPI_DriverPS3ThirdParty_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) +{ +} + +static SDL_bool +HIDAPI_DriverPS3ThirdParty_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) +{ + SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context; + + ctx->joystick = joystick; + SDL_zeroa(ctx->last_state); + + /* Initialize the joystick capabilities */ + joystick->nbuttons = 15; + joystick->naxes = 16; + joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; + + return SDL_TRUE; +} + +static int +HIDAPI_DriverPS3ThirdParty_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +{ + return SDL_Unsupported(); +} + +static int +HIDAPI_DriverPS3ThirdParty_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) +{ + return SDL_Unsupported(); +} + +static Uint32 +HIDAPI_DriverPS3ThirdParty_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) +{ + return 0; +} + +static int +HIDAPI_DriverPS3ThirdParty_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) +{ + return SDL_Unsupported(); +} + +static int +HIDAPI_DriverPS3ThirdParty_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size) +{ + return SDL_Unsupported(); +} + +static int +HIDAPI_DriverPS3ThirdParty_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + +static void +HIDAPI_DriverPS3ThirdParty_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverPS3_Context *ctx, Uint8 *data, int size) +{ + Sint16 axis; + + if (ctx->last_state[0] != data[0]) { + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[0] & 0x01) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[0] & 0x02) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[0] & 0x04) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[0] & 0x08) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[0] & 0x10) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[0] & 0x20) ? SDL_PRESSED : SDL_RELEASED); + } + + if (ctx->last_state[1] != data[1]) { + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[1] & 0x01) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[1] & 0x02) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[1] & 0x10) ? SDL_PRESSED : SDL_RELEASED); + } + + if (ctx->last_state[2] != data[2]) { + SDL_bool dpad_up = SDL_FALSE; + SDL_bool dpad_down = SDL_FALSE; + SDL_bool dpad_left = SDL_FALSE; + SDL_bool dpad_right = SDL_FALSE; + + switch (data[2] & 0x0f) { + case 0: + dpad_up = SDL_TRUE; + break; + case 1: + dpad_up = SDL_TRUE; + dpad_right = SDL_TRUE; + break; + case 2: + dpad_right = SDL_TRUE; + break; + case 3: + dpad_right = SDL_TRUE; + dpad_down = SDL_TRUE; + break; + case 4: + dpad_down = SDL_TRUE; + break; + case 5: + dpad_left = SDL_TRUE; + dpad_down = SDL_TRUE; + break; + case 6: + dpad_left = SDL_TRUE; + break; + case 7: + dpad_up = SDL_TRUE; + dpad_left = SDL_TRUE; + break; + default: + break; + } + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left); + } + + axis = ((int)data[17] * 257) - 32768; + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis); + axis = ((int)data[18] * 257) - 32768; + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis); + axis = ((int)data[3] * 257) - 32768; + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis); + axis = ((int)data[4] * 257) - 32768; + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis); + axis = ((int)data[5] * 257) - 32768; + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis); + axis = ((int)data[6] * 257) - 32768; + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis); + + /* Buttons are mapped as axes in the order they appear in the button enumeration */ + { + static int button_axis_offsets[] = { + 13, /* SDL_CONTROLLER_BUTTON_A */ + 12, /* SDL_CONTROLLER_BUTTON_B */ + 14, /* SDL_CONTROLLER_BUTTON_X */ + 11, /* SDL_CONTROLLER_BUTTON_Y */ + 0, /* SDL_CONTROLLER_BUTTON_BACK */ + 0, /* SDL_CONTROLLER_BUTTON_GUIDE */ + 0, /* SDL_CONTROLLER_BUTTON_START */ + 0, /* SDL_CONTROLLER_BUTTON_LEFTSTICK */ + 0, /* SDL_CONTROLLER_BUTTON_RIGHTSTICK */ + 15, /* SDL_CONTROLLER_BUTTON_LEFTSHOULDER */ + 16, /* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER */ + 9, /* SDL_CONTROLLER_BUTTON_DPAD_UP */ + 10, /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */ + 8, /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */ + 7, /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */ + }; + int i, axis_index = 6; + + for (i = 0; i < SDL_arraysize(button_axis_offsets); ++i) { + int offset = button_axis_offsets[i]; + if (!offset) { + /* This button doesn't report as an axis */ + continue; + } + + axis = ((int)data[offset] * 257) - 32768; + SDL_PrivateJoystickAxis(joystick, axis_index, axis); + ++axis_index; + } + } + + SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); +} + +static SDL_bool +HIDAPI_DriverPS3ThirdParty_UpdateDevice(SDL_HIDAPI_Device *device) +{ + SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context; + SDL_Joystick *joystick = NULL; + Uint8 data[USB_PACKET_LENGTH]; + int size; + + if (device->num_joysticks > 0) { + joystick = SDL_JoystickFromInstanceID(device->joysticks[0]); + } else { + return SDL_FALSE; + } + + while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { +#ifdef DEBUG_PS3_PROTOCOL + HIDAPI_DumpPacket("PS3 packet: size = %d", data, size); +#endif + if (!joystick) { + continue; + } + + if (size == 27) { + HIDAPI_DriverPS3ThirdParty_HandleStatePacket(joystick, ctx, data, size); + } else { +#ifdef DEBUG_JOYSTICK + SDL_Log("Unknown PS3 packet, size %d\n", size); +#endif + } + } + + if (size < 0) { + /* Read error, device is disconnected */ + HIDAPI_JoystickDisconnected(device, device->joysticks[0]); + } + return (size >= 0); +} + +static void +HIDAPI_DriverPS3ThirdParty_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) +{ + SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context; + + ctx->joystick = NULL; +} + +static void +HIDAPI_DriverPS3ThirdParty_FreeDevice(SDL_HIDAPI_Device *device) +{ +} + +SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS3ThirdParty = +{ + SDL_HINT_JOYSTICK_HIDAPI_PS3, + SDL_TRUE, + HIDAPI_DriverPS3_RegisterHints, + HIDAPI_DriverPS3_UnregisterHints, + HIDAPI_DriverPS3ThirdParty_IsEnabled, + HIDAPI_DriverPS3ThirdParty_IsSupportedDevice, + HIDAPI_DriverPS3ThirdParty_InitDevice, + HIDAPI_DriverPS3ThirdParty_GetDevicePlayerIndex, + HIDAPI_DriverPS3ThirdParty_SetDevicePlayerIndex, + HIDAPI_DriverPS3ThirdParty_UpdateDevice, + HIDAPI_DriverPS3ThirdParty_OpenJoystick, + HIDAPI_DriverPS3ThirdParty_RumbleJoystick, + HIDAPI_DriverPS3ThirdParty_RumbleJoystickTriggers, + HIDAPI_DriverPS3ThirdParty_GetJoystickCapabilities, + HIDAPI_DriverPS3ThirdParty_SetJoystickLED, + HIDAPI_DriverPS3ThirdParty_SendJoystickEffect, + HIDAPI_DriverPS3ThirdParty_SetJoystickSensorsEnabled, + HIDAPI_DriverPS3ThirdParty_CloseJoystick, + HIDAPI_DriverPS3ThirdParty_FreeDevice, +}; + #endif /* SDL_JOYSTICK_HIDAPI_PS3 */ #endif /* SDL_JOYSTICK_HIDAPI */ diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index 87bcfce78..f5d4674bb 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -57,6 +57,7 @@ static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = { #endif #ifdef SDL_JOYSTICK_HIDAPI_PS3 &SDL_HIDAPI_DriverPS3, + &SDL_HIDAPI_DriverPS3ThirdParty, #endif #ifdef SDL_JOYSTICK_HIDAPI_PS4 &SDL_HIDAPI_DriverPS4, diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h index 7d2bfd1e8..f1c7b2b35 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h +++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h @@ -129,6 +129,7 @@ extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverJoyCons; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverLuna; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverNintendoClassic; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS3; +extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS3ThirdParty; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS5; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverShield;