Path: blob/master/thirdparty/sdl/joystick/hidapi/SDL_hidapi_ps3.c
9913 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20#include "SDL_internal.h"2122#ifdef SDL_JOYSTICK_HIDAPI2324#include "../../SDL_hints_c.h"25#include "../SDL_sysjoystick.h"26#include "SDL_hidapijoystick_c.h"27#include "SDL_hidapi_rumble.h"2829#ifdef SDL_JOYSTICK_HIDAPI_PS33031// Define this if you want to log all packets from the controller32// #define DEBUG_PS3_PROTOCOL3334#define LOAD16(A, B) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8))3536typedef enum37{38k_EPS3ReportIdState = 1,39k_EPS3ReportIdEffects = 1,40} EPS3ReportId;4142typedef enum43{44k_EPS3SonySixaxisReportIdState = 0,45k_EPS3SonySixaxisReportIdEffects = 0,46} EPS3SonySixaxisReportId;4748// Commands for Sony's sixaxis.sys Windows driver49// All commands must be sent using 49-byte buffer containing output report50// Byte 0 indicates reportId and must always be 051// Byte 1 indicates a command, supported values are specified below:52typedef enum53{54// This command allows to set user LEDs.55// Bytes 5,6.7.8 contain mode for corresponding LED: 0 - LED is off, 1 - LED in on, 2 - LED is flashing.56// Bytes 9-16 specify 64-bit LED flash period in 100 ns units if some LED is flashing, otherwise not used.57k_EPS3SixaxisCommandSetLEDs = 1,5859// This command allows to set left and right motors.60// Byte 5 is right motor duration (0-255) and byte 6, if not zero, activates right motor. Zero value disables right motor.61// Byte 7 is left motor duration (0-255) and byte 8 is left motor amplitude (0-255).62k_EPS3SixaxisCommandSetMotors = 2,6364// This command allows to block/unblock setting device LEDs by applications.65// Byte 5 is used as parameter - any non-zero value blocks LEDs, zero value will unblock LEDs.66k_EPS3SixaxisCommandBlockLEDs = 3,6768// This command refreshes driver settings. No parameters used.69// When sixaxis driver loads it reads 'CurrentDriverSetting' binary value from 'HKLM\System\CurrentControlSet\Services\sixaxis\Parameters' registry key.70// If the key is not present then default values are used. Sending this command forces sixaxis driver to re-read the registry and update driver settings.71k_EPS3SixaxisCommandRefreshDriverSetting = 9,7273// This command clears current bluetooth pairing. No parameters used.74k_EPS3SixaxisCommandClearPairing = 1075} EPS3SixaxisDriverCommands;7677typedef struct78{79SDL_HIDAPI_Device *device;80SDL_Joystick *joystick;81bool is_shanwan;82bool has_analog_buttons;83bool report_sensors;84bool effects_updated;85int player_index;86Uint8 rumble_left;87Uint8 rumble_right;88Uint8 last_state[USB_PACKET_LENGTH];89} SDL_DriverPS3_Context;9091static bool HIDAPI_DriverPS3_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size);9293static void HIDAPI_DriverPS3_RegisterHints(SDL_HintCallback callback, void *userdata)94{95SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS3, callback, userdata);96}9798static void HIDAPI_DriverPS3_UnregisterHints(SDL_HintCallback callback, void *userdata)99{100SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS3, callback, userdata);101}102103static bool HIDAPI_DriverPS3_IsEnabled(void)104{105bool default_value;106107#ifdef SDL_PLATFORM_MACOS108// This works well on macOS109default_value = true;110#elif defined(SDL_PLATFORM_WIN32)111/* For official Sony driver (sixaxis.sys) use SDL_HINT_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER.112*113* See https://github.com/ViGEm/DsHidMini as an alternative driver114*/115default_value = false;116#elif defined(SDL_PLATFORM_LINUX)117/* Linux drivers do a better job of managing the transition between118* USB and Bluetooth. There are also some quirks in communicating119* with PS3 controllers that have been implemented in SDL's hidapi120* for libusb, but are not possible to support using hidraw if the121* kernel doesn't already know about them.122*/123default_value = false;124#else125// Untested, default off126default_value = false;127#endif128129if (default_value) {130default_value = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT);131}132return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS3, default_value);133}134135static bool HIDAPI_DriverPS3_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)136{137if (vendor_id == USB_VENDOR_SONY && product_id == USB_PRODUCT_SONY_DS3) {138return true;139}140if (vendor_id == USB_VENDOR_SHANWAN && product_id == USB_PRODUCT_SHANWAN_DS3) {141return true;142}143return false;144}145146static int ReadFeatureReport(SDL_hid_device *dev, Uint8 report_id, Uint8 *report, size_t length)147{148SDL_memset(report, 0, length);149report[0] = report_id;150return SDL_hid_get_feature_report(dev, report, length);151}152153static int SendFeatureReport(SDL_hid_device *dev, Uint8 *report, size_t length)154{155return SDL_hid_send_feature_report(dev, report, length);156}157158static bool HIDAPI_DriverPS3_InitDevice(SDL_HIDAPI_Device *device)159{160SDL_DriverPS3_Context *ctx;161bool is_shanwan = false;162163if (device->vendor_id == USB_VENDOR_SONY &&164SDL_strncasecmp(device->name, "ShanWan", 7) == 0) {165is_shanwan = true;166}167if (device->vendor_id == USB_VENDOR_SHANWAN ||168device->vendor_id == USB_VENDOR_SHANWAN_ALT) {169is_shanwan = true;170}171172ctx = (SDL_DriverPS3_Context *)SDL_calloc(1, sizeof(*ctx));173if (!ctx) {174return false;175}176ctx->device = device;177ctx->is_shanwan = is_shanwan;178ctx->has_analog_buttons = true;179180device->context = ctx;181182// Set the controller into report mode over Bluetooth183if (device->is_bluetooth) {184Uint8 data[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };185186SendFeatureReport(device->dev, data, sizeof(data));187}188189// Set the controller into report mode over USB190if (!device->is_bluetooth) {191Uint8 data[USB_PACKET_LENGTH];192193int size = ReadFeatureReport(device->dev, 0xf2, data, 17);194if (size < 0) {195SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,196"HIDAPI_DriverPS3_InitDevice(): Couldn't read feature report 0xf2");197return false;198}199#ifdef DEBUG_PS3_PROTOCOL200HIDAPI_DumpPacket("PS3 0xF2 packet: size = %d", data, size);201#endif202size = ReadFeatureReport(device->dev, 0xf5, data, 8);203if (size < 0) {204SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,205"HIDAPI_DriverPS3_InitDevice(): Couldn't read feature report 0xf5");206return false;207}208#ifdef DEBUG_PS3_PROTOCOL209HIDAPI_DumpPacket("PS3 0xF5 packet: size = %d", data, size);210#endif211if (!ctx->is_shanwan) {212// An output report could cause ShanWan controllers to rumble non-stop213SDL_hid_write(device->dev, data, 1);214}215}216217device->type = SDL_GAMEPAD_TYPE_PS3;218HIDAPI_SetDeviceName(device, "PS3 Controller");219220return HIDAPI_JoystickConnected(device, NULL);221}222223static int HIDAPI_DriverPS3_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)224{225return -1;226}227228static bool HIDAPI_DriverPS3_UpdateEffects(SDL_HIDAPI_Device *device)229{230SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;231232Uint8 effects[] = {2330x01, 0xff, 0x00, 0xff, 0x00,2340x00, 0x00, 0x00, 0x00, 0x00,2350xff, 0x27, 0x10, 0x00, 0x32,2360xff, 0x27, 0x10, 0x00, 0x32,2370xff, 0x27, 0x10, 0x00, 0x32,2380xff, 0x27, 0x10, 0x00, 0x32,2390x00, 0x00, 0x00, 0x00, 0x00240};241242effects[2] = ctx->rumble_right ? 1 : 0;243effects[4] = ctx->rumble_left;244245effects[9] = (0x01 << (1 + (ctx->player_index % 4)));246247return HIDAPI_DriverPS3_SendJoystickEffect(device, ctx->joystick, effects, sizeof(effects));248}249250static void HIDAPI_DriverPS3_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)251{252SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;253254if (!ctx) {255return;256}257258ctx->player_index = player_index;259260// This will set the new LED state based on the new player index261HIDAPI_DriverPS3_UpdateEffects(device);262}263264static bool HIDAPI_DriverPS3_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)265{266SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;267268SDL_AssertJoysticksLocked();269270ctx->joystick = joystick;271ctx->effects_updated = false;272ctx->rumble_left = 0;273ctx->rumble_right = 0;274SDL_zeroa(ctx->last_state);275276// Initialize player index (needed for setting LEDs)277ctx->player_index = SDL_GetJoystickPlayerIndex(joystick);278279// Initialize the joystick capabilities280joystick->nbuttons = 11;281joystick->naxes = 6;282if (ctx->has_analog_buttons) {283joystick->naxes += 10;284}285joystick->nhats = 1;286287SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 100.0f);288289return true;290}291292static bool HIDAPI_DriverPS3_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)293{294SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;295296ctx->rumble_left = (low_frequency_rumble >> 8);297ctx->rumble_right = (high_frequency_rumble >> 8);298299return HIDAPI_DriverPS3_UpdateEffects(device);300}301302static bool HIDAPI_DriverPS3_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)303{304return SDL_Unsupported();305}306307static Uint32 HIDAPI_DriverPS3_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)308{309return SDL_JOYSTICK_CAP_RUMBLE;310}311312static bool HIDAPI_DriverPS3_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)313{314return SDL_Unsupported();315}316317static bool HIDAPI_DriverPS3_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size)318{319Uint8 data[49];320int report_size, offset;321322SDL_zeroa(data);323324data[0] = k_EPS3ReportIdEffects;325report_size = sizeof(data);326offset = 1;327SDL_memcpy(&data[offset], effect, SDL_min((sizeof(data) - offset), (size_t)size));328329if (SDL_HIDAPI_SendRumble(device, data, report_size) != report_size) {330return SDL_SetError("Couldn't send rumble packet");331}332return true;333}334335static bool HIDAPI_DriverPS3_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)336{337SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;338339ctx->report_sensors = enabled;340341return true;342}343344static float HIDAPI_DriverPS3_ScaleAccel(Sint16 value)345{346// Accelerometer values are in big endian order347value = SDL_Swap16BE(value);348return ((float)(value - 511) / 113.0f) * SDL_STANDARD_GRAVITY;349}350351static void HIDAPI_DriverPS3_HandleMiniStatePacket(SDL_Joystick *joystick, SDL_DriverPS3_Context *ctx, Uint8 *data, int size)352{353Sint16 axis;354Uint64 timestamp = SDL_GetTicksNS();355356if (ctx->last_state[4] != data[4]) {357Uint8 hat;358359switch (data[4] & 0x0f) {360case 0:361hat = SDL_HAT_UP;362break;363case 1:364hat = SDL_HAT_RIGHTUP;365break;366case 2:367hat = SDL_HAT_RIGHT;368break;369case 3:370hat = SDL_HAT_RIGHTDOWN;371break;372case 4:373hat = SDL_HAT_DOWN;374break;375case 5:376hat = SDL_HAT_LEFTDOWN;377break;378case 6:379hat = SDL_HAT_LEFT;380break;381case 7:382hat = SDL_HAT_LEFTUP;383break;384default:385hat = SDL_HAT_CENTERED;386break;387}388SDL_SendJoystickHat(timestamp, joystick, 0, hat);389390SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[4] & 0x10) != 0));391SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[4] & 0x20) != 0));392SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[4] & 0x40) != 0));393SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[4] & 0x80) != 0));394}395396if (ctx->last_state[5] != data[5]) {397SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[5] & 0x01) != 0));398SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[5] & 0x02) != 0));399SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (data[5] & 0x04) ? SDL_JOYSTICK_AXIS_MAX : SDL_JOYSTICK_AXIS_MIN);400SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, (data[5] & 0x08) ? SDL_JOYSTICK_AXIS_MAX : SDL_JOYSTICK_AXIS_MIN);401SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[5] & 0x10) != 0));402SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[5] & 0x20) != 0));403SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[5] & 0x40) != 0));404SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[5] & 0x80) != 0));405}406407axis = ((int)data[2] * 257) - 32768;408SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);409axis = ((int)data[3] * 257) - 32768;410SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);411axis = ((int)data[0] * 257) - 32768;412SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);413axis = ((int)data[1] * 257) - 32768;414SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);415416SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));417}418419static void HIDAPI_DriverPS3_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverPS3_Context *ctx, Uint8 *data, int size)420{421Sint16 axis;422Uint64 timestamp = SDL_GetTicksNS();423424if (ctx->last_state[2] != data[2]) {425Uint8 hat = 0;426427SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[2] & 0x01) != 0));428SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[2] & 0x02) != 0));429SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[2] & 0x04) != 0));430SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[2] & 0x08) != 0));431432if (data[2] & 0x10) {433hat |= SDL_HAT_UP;434}435if (data[2] & 0x20) {436hat |= SDL_HAT_RIGHT;437}438if (data[2] & 0x40) {439hat |= SDL_HAT_DOWN;440}441if (data[2] & 0x80) {442hat |= SDL_HAT_LEFT;443}444SDL_SendJoystickHat(timestamp, joystick, 0, hat);445}446447if (ctx->last_state[3] != data[3]) {448SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[3] & 0x04) != 0));449SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[3] & 0x08) != 0));450SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[3] & 0x10) != 0));451SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[3] & 0x20) != 0));452SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[3] & 0x40) != 0));453SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[3] & 0x80) != 0));454}455456if (ctx->last_state[4] != data[4]) {457SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[4] & 0x01) != 0));458}459460axis = ((int)data[18] * 257) - 32768;461SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);462axis = ((int)data[19] * 257) - 32768;463SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);464axis = ((int)data[6] * 257) - 32768;465SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);466axis = ((int)data[7] * 257) - 32768;467SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);468axis = ((int)data[8] * 257) - 32768;469SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);470axis = ((int)data[9] * 257) - 32768;471SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);472473// Buttons are mapped as axes in the order they appear in the button enumeration474if (ctx->has_analog_buttons) {475static int button_axis_offsets[] = {47624, // SDL_GAMEPAD_BUTTON_SOUTH47723, // SDL_GAMEPAD_BUTTON_EAST47825, // SDL_GAMEPAD_BUTTON_WEST47922, // SDL_GAMEPAD_BUTTON_NORTH4800, // SDL_GAMEPAD_BUTTON_BACK4810, // SDL_GAMEPAD_BUTTON_GUIDE4820, // SDL_GAMEPAD_BUTTON_START4830, // SDL_GAMEPAD_BUTTON_LEFT_STICK4840, // SDL_GAMEPAD_BUTTON_RIGHT_STICK48520, // SDL_GAMEPAD_BUTTON_LEFT_SHOULDER48621, // SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER48714, // SDL_GAMEPAD_BUTTON_DPAD_UP48816, // SDL_GAMEPAD_BUTTON_DPAD_DOWN48917, // SDL_GAMEPAD_BUTTON_DPAD_LEFT49015, // SDL_GAMEPAD_BUTTON_DPAD_RIGHT491};492Uint8 i, axis_index = 6;493494for (i = 0; i < SDL_arraysize(button_axis_offsets); ++i) {495int offset = button_axis_offsets[i];496if (!offset) {497// This button doesn't report as an axis498continue;499}500501axis = ((int)data[offset] * 257) - 32768;502SDL_SendJoystickAxis(timestamp, joystick, axis_index, axis);503++axis_index;504}505}506507if (ctx->report_sensors) {508float sensor_data[3];509510sensor_data[0] = HIDAPI_DriverPS3_ScaleAccel(LOAD16(data[41], data[42]));511sensor_data[1] = -HIDAPI_DriverPS3_ScaleAccel(LOAD16(data[45], data[46]));512sensor_data[2] = -HIDAPI_DriverPS3_ScaleAccel(LOAD16(data[43], data[44]));513SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, timestamp, sensor_data, SDL_arraysize(sensor_data));514}515516SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));517}518519static bool HIDAPI_DriverPS3_UpdateDevice(SDL_HIDAPI_Device *device)520{521SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;522SDL_Joystick *joystick = NULL;523Uint8 data[USB_PACKET_LENGTH];524int size;525526if (device->num_joysticks > 0) {527joystick = SDL_GetJoystickFromID(device->joysticks[0]);528} else {529return false;530}531532while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {533#ifdef DEBUG_PS3_PROTOCOL534HIDAPI_DumpPacket("PS3 packet: size = %d", data, size);535#endif536if (!joystick) {537continue;538}539540if (size == 7) {541// Seen on a ShanWan PS2 -> PS3 USB converter542HIDAPI_DriverPS3_HandleMiniStatePacket(joystick, ctx, data, size);543544// Wait for the first report to set the LED state after the controller stops blinking545if (!ctx->effects_updated) {546HIDAPI_DriverPS3_UpdateEffects(device);547ctx->effects_updated = true;548}549continue;550}551552switch (data[0]) {553case k_EPS3ReportIdState:554if (data[1] == 0xFF) {555// Invalid data packet, ignore556break;557}558HIDAPI_DriverPS3_HandleStatePacket(joystick, ctx, data, size);559560// Wait for the first report to set the LED state after the controller stops blinking561if (!ctx->effects_updated) {562HIDAPI_DriverPS3_UpdateEffects(device);563ctx->effects_updated = true;564}565break;566default:567#ifdef DEBUG_JOYSTICK568SDL_Log("Unknown PS3 packet: 0x%.2x", data[0]);569#endif570break;571}572}573574if (size < 0) {575// Read error, device is disconnected576HIDAPI_JoystickDisconnected(device, device->joysticks[0]);577}578return (size >= 0);579}580581static void HIDAPI_DriverPS3_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)582{583SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;584585ctx->joystick = NULL;586}587588static void HIDAPI_DriverPS3_FreeDevice(SDL_HIDAPI_Device *device)589{590}591592SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS3 = {593SDL_HINT_JOYSTICK_HIDAPI_PS3,594true,595HIDAPI_DriverPS3_RegisterHints,596HIDAPI_DriverPS3_UnregisterHints,597HIDAPI_DriverPS3_IsEnabled,598HIDAPI_DriverPS3_IsSupportedDevice,599HIDAPI_DriverPS3_InitDevice,600HIDAPI_DriverPS3_GetDevicePlayerIndex,601HIDAPI_DriverPS3_SetDevicePlayerIndex,602HIDAPI_DriverPS3_UpdateDevice,603HIDAPI_DriverPS3_OpenJoystick,604HIDAPI_DriverPS3_RumbleJoystick,605HIDAPI_DriverPS3_RumbleJoystickTriggers,606HIDAPI_DriverPS3_GetJoystickCapabilities,607HIDAPI_DriverPS3_SetJoystickLED,608HIDAPI_DriverPS3_SendJoystickEffect,609HIDAPI_DriverPS3_SetJoystickSensorsEnabled,610HIDAPI_DriverPS3_CloseJoystick,611HIDAPI_DriverPS3_FreeDevice,612};613614static bool HIDAPI_DriverPS3ThirdParty_IsEnabled(void)615{616return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS3,617SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI,618SDL_HIDAPI_DEFAULT));619}620621static bool HIDAPI_DriverPS3ThirdParty_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)622{623Uint8 data[USB_PACKET_LENGTH];624int size;625626if (vendor_id == USB_VENDOR_LOGITECH &&627product_id == USB_PRODUCT_LOGITECH_CHILLSTREAM) {628return true;629}630631if ((type == SDL_GAMEPAD_TYPE_PS3 && vendor_id != USB_VENDOR_SONY) ||632HIDAPI_SupportsPlaystationDetection(vendor_id, product_id)) {633if (device && device->dev) {634size = ReadFeatureReport(device->dev, 0x03, data, sizeof(data));635if (size == 8 && data[2] == 0x26) {636// Supported third party controller637return true;638} else {639return false;640}641} else {642// Might be supported by this driver, enumerate and find out643return true;644}645}646return false;647}648649static bool HIDAPI_DriverPS3ThirdParty_InitDevice(SDL_HIDAPI_Device *device)650{651SDL_DriverPS3_Context *ctx;652653ctx = (SDL_DriverPS3_Context *)SDL_calloc(1, sizeof(*ctx));654if (!ctx) {655return false;656}657ctx->device = device;658if (device->vendor_id == USB_VENDOR_SWITCH && device->product_id == USB_PRODUCT_SWITCH_RETROBIT_CONTROLLER) {659ctx->has_analog_buttons = false;660} else {661ctx->has_analog_buttons = true;662}663664device->context = ctx;665666device->type = SDL_GAMEPAD_TYPE_PS3;667668if (device->vendor_id == USB_VENDOR_LOGITECH &&669device->product_id == USB_PRODUCT_LOGITECH_CHILLSTREAM) {670HIDAPI_SetDeviceName(device, "Logitech ChillStream");671}672673return HIDAPI_JoystickConnected(device, NULL);674}675676static int HIDAPI_DriverPS3ThirdParty_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)677{678return -1;679}680681static void HIDAPI_DriverPS3ThirdParty_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)682{683}684685static bool HIDAPI_DriverPS3ThirdParty_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)686{687SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;688689SDL_AssertJoysticksLocked();690691ctx->joystick = joystick;692SDL_zeroa(ctx->last_state);693694// Initialize the joystick capabilities695joystick->nbuttons = 11;696joystick->naxes = 6;697if (ctx->has_analog_buttons) {698joystick->naxes += 10;699}700joystick->nhats = 1;701702if (device->vendor_id == USB_VENDOR_SWITCH && device->product_id == USB_PRODUCT_SWITCH_RETROBIT_CONTROLLER) {703// This is a wireless controller using a USB dongle704joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;705}706707return true;708}709710static bool HIDAPI_DriverPS3ThirdParty_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)711{712return SDL_Unsupported();713}714715static bool HIDAPI_DriverPS3ThirdParty_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)716{717return SDL_Unsupported();718}719720static Uint32 HIDAPI_DriverPS3ThirdParty_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)721{722return 0;723}724725static bool HIDAPI_DriverPS3ThirdParty_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)726{727return SDL_Unsupported();728}729730static bool HIDAPI_DriverPS3ThirdParty_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size)731{732return SDL_Unsupported();733}734735static bool HIDAPI_DriverPS3ThirdParty_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)736{737return SDL_Unsupported();738}739740static void HIDAPI_DriverPS3ThirdParty_HandleStatePacket18(SDL_Joystick *joystick, SDL_DriverPS3_Context *ctx, Uint8 *data, int size)741{742Sint16 axis;743Uint64 timestamp = SDL_GetTicksNS();744745if (ctx->last_state[0] != data[0]) {746SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[0] & 0x01) != 0));747SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[0] & 0x02) != 0));748SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[0] & 0x04) != 0));749SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[0] & 0x08) != 0));750SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[0] & 0x10) != 0));751SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[0] & 0x20) != 0));752}753754if (ctx->last_state[1] != data[1]) {755Uint8 hat;756757SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[1] & 0x01) != 0));758SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[1] & 0x02) != 0));759SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[1] & 0x04) != 0));760SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[1] & 0x08) != 0));761762switch (data[1] >> 4) {763case 0:764hat = SDL_HAT_UP;765break;766case 1:767hat = SDL_HAT_RIGHTUP;768break;769case 2:770hat = SDL_HAT_RIGHT;771break;772case 3:773hat = SDL_HAT_RIGHTDOWN;774break;775case 4:776hat = SDL_HAT_DOWN;777break;778case 5:779hat = SDL_HAT_LEFTDOWN;780break;781case 6:782hat = SDL_HAT_LEFT;783break;784case 7:785hat = SDL_HAT_LEFTUP;786break;787default:788hat = SDL_HAT_CENTERED;789break;790}791SDL_SendJoystickHat(timestamp, joystick, 0, hat);792}793794axis = ((int)data[16] * 257) - 32768;795SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);796axis = ((int)data[17] * 257) - 32768;797SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);798axis = ((int)data[2] * 257) - 32768;799SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);800axis = ((int)data[3] * 257) - 32768;801SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);802axis = ((int)data[4] * 257) - 32768;803SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);804axis = ((int)data[5] * 257) - 32768;805SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);806807// Buttons are mapped as axes in the order they appear in the button enumeration808if (ctx->has_analog_buttons) {809static int button_axis_offsets[] = {81012, // SDL_GAMEPAD_BUTTON_SOUTH81111, // SDL_GAMEPAD_BUTTON_EAST81213, // SDL_GAMEPAD_BUTTON_WEST81310, // SDL_GAMEPAD_BUTTON_NORTH8140, // SDL_GAMEPAD_BUTTON_BACK8150, // SDL_GAMEPAD_BUTTON_GUIDE8160, // SDL_GAMEPAD_BUTTON_START8170, // SDL_GAMEPAD_BUTTON_LEFT_STICK8180, // SDL_GAMEPAD_BUTTON_RIGHT_STICK81914, // SDL_GAMEPAD_BUTTON_LEFT_SHOULDER82015, // SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER8218, // SDL_GAMEPAD_BUTTON_DPAD_UP8229, // SDL_GAMEPAD_BUTTON_DPAD_DOWN8237, // SDL_GAMEPAD_BUTTON_DPAD_LEFT8246, // SDL_GAMEPAD_BUTTON_DPAD_RIGHT825};826Uint8 i, axis_index = 6;827828for (i = 0; i < SDL_arraysize(button_axis_offsets); ++i) {829int offset = button_axis_offsets[i];830if (!offset) {831// This button doesn't report as an axis832continue;833}834835axis = ((int)data[offset] * 257) - 32768;836SDL_SendJoystickAxis(timestamp, joystick, axis_index, axis);837++axis_index;838}839}840841SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));842}843844static void HIDAPI_DriverPS3ThirdParty_HandleStatePacket19(SDL_Joystick *joystick, SDL_DriverPS3_Context *ctx, Uint8 *data, int size)845{846Sint16 axis;847Uint64 timestamp = SDL_GetTicksNS();848849if (ctx->last_state[0] != data[0]) {850SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[0] & 0x01) != 0));851SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[0] & 0x02) != 0));852SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[0] & 0x04) != 0));853SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[0] & 0x08) != 0));854SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[0] & 0x10) != 0));855SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[0] & 0x20) != 0));856}857858if (ctx->last_state[1] != data[1]) {859SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[1] & 0x01) != 0));860SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[1] & 0x02) != 0));861SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[1] & 0x04) != 0));862SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[1] & 0x08) != 0));863SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[1] & 0x10) != 0));864}865866if (ctx->device->vendor_id == USB_VENDOR_SAITEK && ctx->device->product_id == USB_PRODUCT_SAITEK_CYBORG_V3) {867// Cyborg V.3 Rumble Pad doesn't set the dpad bits as expected, so use the axes instead868Uint8 hat = 0;869870if (data[7]) {871hat |= SDL_HAT_RIGHT;872}873if (data[8]) {874hat |= SDL_HAT_LEFT;875}876if (data[9]) {877hat |= SDL_HAT_UP;878}879if (data[10]) {880hat |= SDL_HAT_DOWN;881}882SDL_SendJoystickHat(timestamp, joystick, 0, hat);883} else {884if (ctx->last_state[2] != data[2]) {885Uint8 hat;886887switch (data[2] & 0x0f) {888case 0:889hat = SDL_HAT_UP;890break;891case 1:892hat = SDL_HAT_RIGHTUP;893break;894case 2:895hat = SDL_HAT_RIGHT;896break;897case 3:898hat = SDL_HAT_RIGHTDOWN;899break;900case 4:901hat = SDL_HAT_DOWN;902break;903case 5:904hat = SDL_HAT_LEFTDOWN;905break;906case 6:907hat = SDL_HAT_LEFT;908break;909case 7:910hat = SDL_HAT_LEFTUP;911break;912default:913hat = SDL_HAT_CENTERED;914break;915}916SDL_SendJoystickHat(timestamp, joystick, 0, hat);917}918}919920if (data[0] & 0x40) {921axis = 32767;922} else {923axis = ((int)data[17] * 257) - 32768;924}925SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);926if (data[0] & 0x80) {927axis = 32767;928} else {929axis = ((int)data[18] * 257) - 32768;930}931SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);932axis = ((int)data[3] * 257) - 32768;933SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);934axis = ((int)data[4] * 257) - 32768;935SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);936axis = ((int)data[5] * 257) - 32768;937SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);938axis = ((int)data[6] * 257) - 32768;939SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);940941// Buttons are mapped as axes in the order they appear in the button enumeration942if (ctx->has_analog_buttons) {943static int button_axis_offsets[] = {94413, // SDL_GAMEPAD_BUTTON_SOUTH94512, // SDL_GAMEPAD_BUTTON_EAST94614, // SDL_GAMEPAD_BUTTON_WEST94711, // SDL_GAMEPAD_BUTTON_NORTH9480, // SDL_GAMEPAD_BUTTON_BACK9490, // SDL_GAMEPAD_BUTTON_GUIDE9500, // SDL_GAMEPAD_BUTTON_START9510, // SDL_GAMEPAD_BUTTON_LEFT_STICK9520, // SDL_GAMEPAD_BUTTON_RIGHT_STICK95315, // SDL_GAMEPAD_BUTTON_LEFT_SHOULDER95416, // SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER9559, // SDL_GAMEPAD_BUTTON_DPAD_UP95610, // SDL_GAMEPAD_BUTTON_DPAD_DOWN9578, // SDL_GAMEPAD_BUTTON_DPAD_LEFT9587, // SDL_GAMEPAD_BUTTON_DPAD_RIGHT959};960Uint8 i, axis_index = 6;961962for (i = 0; i < SDL_arraysize(button_axis_offsets); ++i) {963int offset = button_axis_offsets[i];964if (!offset) {965// This button doesn't report as an axis966continue;967}968969axis = ((int)data[offset] * 257) - 32768;970SDL_SendJoystickAxis(timestamp, joystick, axis_index, axis);971++axis_index;972}973}974975SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));976}977978static bool HIDAPI_DriverPS3ThirdParty_UpdateDevice(SDL_HIDAPI_Device *device)979{980SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;981SDL_Joystick *joystick = NULL;982Uint8 data[USB_PACKET_LENGTH];983int size;984985if (device->num_joysticks > 0) {986joystick = SDL_GetJoystickFromID(device->joysticks[0]);987} else {988return false;989}990991while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {992#ifdef DEBUG_PS3_PROTOCOL993HIDAPI_DumpPacket("PS3 packet: size = %d", data, size);994#endif995if (!joystick) {996continue;997}998999if (size >= 19) {1000HIDAPI_DriverPS3ThirdParty_HandleStatePacket19(joystick, ctx, data, size);1001} else if (size == 18) {1002// This packet format was seen with the Logitech ChillStream1003HIDAPI_DriverPS3ThirdParty_HandleStatePacket18(joystick, ctx, data, size);1004} else {1005#ifdef DEBUG_JOYSTICK1006SDL_Log("Unknown PS3 packet, size %d", size);1007#endif1008}1009}10101011if (size < 0) {1012// Read error, device is disconnected1013HIDAPI_JoystickDisconnected(device, device->joysticks[0]);1014}1015return (size >= 0);1016}10171018static void HIDAPI_DriverPS3ThirdParty_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)1019{1020SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;10211022ctx->joystick = NULL;1023}10241025static void HIDAPI_DriverPS3ThirdParty_FreeDevice(SDL_HIDAPI_Device *device)1026{1027}10281029SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS3ThirdParty = {1030SDL_HINT_JOYSTICK_HIDAPI_PS3,1031true,1032HIDAPI_DriverPS3_RegisterHints,1033HIDAPI_DriverPS3_UnregisterHints,1034HIDAPI_DriverPS3ThirdParty_IsEnabled,1035HIDAPI_DriverPS3ThirdParty_IsSupportedDevice,1036HIDAPI_DriverPS3ThirdParty_InitDevice,1037HIDAPI_DriverPS3ThirdParty_GetDevicePlayerIndex,1038HIDAPI_DriverPS3ThirdParty_SetDevicePlayerIndex,1039HIDAPI_DriverPS3ThirdParty_UpdateDevice,1040HIDAPI_DriverPS3ThirdParty_OpenJoystick,1041HIDAPI_DriverPS3ThirdParty_RumbleJoystick,1042HIDAPI_DriverPS3ThirdParty_RumbleJoystickTriggers,1043HIDAPI_DriverPS3ThirdParty_GetJoystickCapabilities,1044HIDAPI_DriverPS3ThirdParty_SetJoystickLED,1045HIDAPI_DriverPS3ThirdParty_SendJoystickEffect,1046HIDAPI_DriverPS3ThirdParty_SetJoystickSensorsEnabled,1047HIDAPI_DriverPS3ThirdParty_CloseJoystick,1048HIDAPI_DriverPS3ThirdParty_FreeDevice,1049};10501051static bool HIDAPI_DriverPS3_UpdateRumbleSonySixaxis(SDL_HIDAPI_Device *device);1052static bool HIDAPI_DriverPS3_UpdateLEDsSonySixaxis(SDL_HIDAPI_Device *device);10531054static void HIDAPI_DriverPS3SonySixaxis_RegisterHints(SDL_HintCallback callback, void *userdata)1055{1056SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER, callback, userdata);1057}10581059static void HIDAPI_DriverPS3SonySixaxis_UnregisterHints(SDL_HintCallback callback, void *userdata)1060{1061SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER, callback, userdata);1062}10631064static bool HIDAPI_DriverPS3SonySixaxis_IsEnabled(void)1065{1066#ifdef SDL_PLATFORM_WIN321067return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER, false);1068#else1069return false;1070#endif1071}10721073static bool HIDAPI_DriverPS3SonySixaxis_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)1074{1075if (vendor_id == USB_VENDOR_SONY && product_id == USB_PRODUCT_SONY_DS3) {1076return true;1077}1078return false;1079}10801081static bool HIDAPI_DriverPS3SonySixaxis_InitDevice(SDL_HIDAPI_Device *device)1082{1083SDL_DriverPS3_Context *ctx;10841085ctx = (SDL_DriverPS3_Context *)SDL_calloc(1, sizeof(*ctx));1086if (!ctx) {1087return false;1088}1089ctx->device = device;1090ctx->has_analog_buttons = true;10911092device->context = ctx;10931094Uint8 data[USB_PACKET_LENGTH];10951096int size = ReadFeatureReport(device->dev, 0xf2, data, sizeof(data));1097if (size < 0) {1098SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,1099"HIDAPI_DriverPS3SonySixaxis_InitDevice(): Couldn't read feature report 0xf2. Trying again with 0x0.");1100SDL_zeroa(data);1101size = ReadFeatureReport(device->dev, 0x00, data, sizeof(data));1102if (size < 0) {1103SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,1104"HIDAPI_DriverPS3SonySixaxis_InitDevice(): Couldn't read feature report 0x00.");1105return false;1106}1107#ifdef DEBUG_PS3_PROTOCOL1108HIDAPI_DumpPacket("PS3 0x0 packet: size = %d", data, size);1109#endif1110}1111#ifdef DEBUG_PS3_PROTOCOL1112HIDAPI_DumpPacket("PS3 0xF2 packet: size = %d", data, size);1113#endif11141115device->type = SDL_GAMEPAD_TYPE_PS3;1116HIDAPI_SetDeviceName(device, "PS3 Controller");11171118return HIDAPI_JoystickConnected(device, NULL);1119}11201121static int HIDAPI_DriverPS3SonySixaxis_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)1122{1123return -1;1124}11251126static void HIDAPI_DriverPS3SonySixaxis_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)1127{1128SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;11291130if (!ctx) {1131return;1132}11331134ctx->player_index = player_index;11351136// This will set the new LED state based on the new player index1137HIDAPI_DriverPS3_UpdateLEDsSonySixaxis(device);1138}11391140static bool HIDAPI_DriverPS3SonySixaxis_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)1141{1142SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;11431144SDL_AssertJoysticksLocked();11451146ctx->joystick = joystick;1147ctx->effects_updated = false;1148ctx->rumble_left = 0;1149ctx->rumble_right = 0;1150SDL_zeroa(ctx->last_state);11511152// Initialize player index (needed for setting LEDs)1153ctx->player_index = SDL_GetJoystickPlayerIndex(joystick);11541155// Initialize the joystick capabilities1156joystick->nbuttons = 11;1157joystick->naxes = 6;1158if (ctx->has_analog_buttons) {1159joystick->naxes += 10;1160}1161joystick->nhats = 1;11621163SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 100.0f);11641165return true;1166}11671168static bool HIDAPI_DriverPS3SonySixaxis_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)1169{1170SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;11711172ctx->rumble_left = (low_frequency_rumble >> 8);1173ctx->rumble_right = (high_frequency_rumble >> 8);11741175return HIDAPI_DriverPS3_UpdateRumbleSonySixaxis(device);1176}11771178static bool HIDAPI_DriverPS3SonySixaxis_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)1179{1180return SDL_Unsupported();1181}11821183static Uint32 HIDAPI_DriverPS3SonySixaxis_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)1184{1185return 0;1186}11871188static bool HIDAPI_DriverPS3SonySixaxis_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)1189{1190return SDL_Unsupported();1191}11921193static bool HIDAPI_DriverPS3SonySixaxis_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size)1194{1195Uint8 data[49];1196int report_size;11971198SDL_zeroa(data);11991200data[0] = k_EPS3SonySixaxisReportIdEffects;1201report_size = sizeof(data);12021203// No offset with Sony sixaxis.sys driver1204SDL_memcpy(&data, effect, SDL_min(sizeof(data), (size_t)size));12051206if (SDL_HIDAPI_SendRumble(device, data, report_size) != report_size) {1207return SDL_SetError("Couldn't send rumble packet");1208}1209return true;1210}12111212static bool HIDAPI_DriverPS3SonySixaxis_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)1213{1214SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;12151216ctx->report_sensors = enabled;12171218return true;1219}12201221static void HIDAPI_DriverPS3SonySixaxis_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverPS3_Context *ctx, Uint8 *data, int size)1222{1223Sint16 axis;1224Uint64 timestamp = SDL_GetTicksNS();12251226if (ctx->last_state[2] != data[2]) {1227Uint8 hat = 0;12281229SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[2] & 0x01) != 0));1230SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[2] & 0x02) != 0));1231SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[2] & 0x04) != 0));1232SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[2] & 0x08) != 0));12331234if (data[2] & 0x10) {1235hat |= SDL_HAT_UP;1236}1237if (data[2] & 0x20) {1238hat |= SDL_HAT_RIGHT;1239}1240if (data[2] & 0x40) {1241hat |= SDL_HAT_DOWN;1242}1243if (data[2] & 0x80) {1244hat |= SDL_HAT_LEFT;1245}1246SDL_SendJoystickHat(timestamp, joystick, 0, hat);1247}12481249if (ctx->last_state[3] != data[3]) {1250SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[3] & 0x04) != 0));1251SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[3] & 0x08) != 0));1252SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[3] & 0x10) != 0));1253SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[3] & 0x20) != 0));1254SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[3] & 0x40) != 0));1255SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[3] & 0x80) != 0));1256}12571258if (ctx->last_state[4] != data[4]) {1259SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[4] & 0x01) != 0));1260}12611262axis = ((int)data[18] * 257) - 32768;1263SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);1264axis = ((int)data[19] * 257) - 32768;1265SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);1266axis = ((int)data[6] * 257) - 32768;1267SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);1268axis = ((int)data[7] * 257) - 32768;1269SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);1270axis = ((int)data[8] * 257) - 32768;1271SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);1272axis = ((int)data[9] * 257) - 32768;1273SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);12741275// Buttons are mapped as axes in the order they appear in the button enumeration1276if (ctx->has_analog_buttons) {1277static int button_axis_offsets[] = {127824, // SDL_GAMEPAD_BUTTON_SOUTH127923, // SDL_GAMEPAD_BUTTON_EAST128025, // SDL_GAMEPAD_BUTTON_WEST128122, // SDL_GAMEPAD_BUTTON_NORTH12820, // SDL_GAMEPAD_BUTTON_BACK12830, // SDL_GAMEPAD_BUTTON_GUIDE12840, // SDL_GAMEPAD_BUTTON_START12850, // SDL_GAMEPAD_BUTTON_LEFT_STICK12860, // SDL_GAMEPAD_BUTTON_RIGHT_STICK128720, // SDL_GAMEPAD_BUTTON_LEFT_SHOULDER128821, // SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER128914, // SDL_GAMEPAD_BUTTON_DPAD_UP129016, // SDL_GAMEPAD_BUTTON_DPAD_DOWN129117, // SDL_GAMEPAD_BUTTON_DPAD_LEFT129215, // SDL_GAMEPAD_BUTTON_DPAD_RIGHT1293};1294Uint8 i, axis_index = 6;12951296for (i = 0; i < SDL_arraysize(button_axis_offsets); ++i) {1297int offset = button_axis_offsets[i];1298if (!offset) {1299// This button doesn't report as an axis1300continue;1301}13021303axis = ((int)data[offset] * 257) - 32768;1304SDL_SendJoystickAxis(timestamp, joystick, axis_index, axis);1305++axis_index;1306}1307}13081309if (ctx->report_sensors) {1310float sensor_data[3];13111312sensor_data[0] = HIDAPI_DriverPS3_ScaleAccel(LOAD16(data[41], data[42]));1313sensor_data[1] = -HIDAPI_DriverPS3_ScaleAccel(LOAD16(data[45], data[46]));1314sensor_data[2] = -HIDAPI_DriverPS3_ScaleAccel(LOAD16(data[43], data[44]));1315SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, timestamp, sensor_data, SDL_arraysize(sensor_data));1316}13171318SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));1319}13201321static bool HIDAPI_DriverPS3SonySixaxis_UpdateDevice(SDL_HIDAPI_Device *device)1322{1323SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;1324SDL_Joystick *joystick = NULL;1325Uint8 data[USB_PACKET_LENGTH];1326int size;13271328if (device->num_joysticks > 0) {1329joystick = SDL_GetJoystickFromID(device->joysticks[0]);1330} else {1331return false;1332}13331334if (!joystick) {1335return false;1336}13371338// With sixaxis.sys driver we need to use hid_get_feature_report instead of hid_read1339size = ReadFeatureReport(device->dev, 0x0, data, sizeof(data));1340if (size < 0) {1341SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,1342"HIDAPI_DriverPS3SonySixaxis_UpdateDevice(): Couldn't read feature report 0x00");1343return false;1344}13451346switch (data[0]) {1347case k_EPS3SonySixaxisReportIdState:1348HIDAPI_DriverPS3SonySixaxis_HandleStatePacket(joystick, ctx, &data[1], size - 1); // report data starts in data[1]13491350// Wait for the first report to set the LED state after the controller stops blinking1351if (!ctx->effects_updated) {1352HIDAPI_DriverPS3_UpdateLEDsSonySixaxis(device);1353ctx->effects_updated = true;1354}13551356break;1357default:1358#ifdef DEBUG_JOYSTICK1359SDL_Log("Unknown PS3 packet: 0x%.2x", data[0]);1360#endif1361break;1362}13631364if (size < 0) {1365// Read error, device is disconnected1366HIDAPI_JoystickDisconnected(device, device->joysticks[0]);1367}1368return (size >= 0);1369}13701371static void HIDAPI_DriverPS3SonySixaxis_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)1372{1373SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;13741375ctx->joystick = NULL;1376}13771378static void HIDAPI_DriverPS3SonySixaxis_FreeDevice(SDL_HIDAPI_Device *device)1379{1380}13811382static bool HIDAPI_DriverPS3_UpdateRumbleSonySixaxis(SDL_HIDAPI_Device *device)1383{1384SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;13851386Uint8 effects[] = {13870x0, // Report Id1388k_EPS3SixaxisCommandSetMotors, // 2 = Set Motors13890x00, 0x00, 0x00, // padding13900xff, // Small Motor duration - 0xff is forever13910x00, // Small Motor off/on (0 or 1)13920xff, // Large Motor duration - 0xff is forever13930x00 // Large Motor force (0 to 255)1394};13951396effects[6] = ctx->rumble_right ? 1 : 0; // Small motor1397effects[8] = ctx->rumble_left; // Large motor13981399return HIDAPI_DriverPS3SonySixaxis_SendJoystickEffect(device, ctx->joystick, effects, sizeof(effects));1400}14011402static bool HIDAPI_DriverPS3_UpdateLEDsSonySixaxis(SDL_HIDAPI_Device *device)1403{1404SDL_DriverPS3_Context *ctx = (SDL_DriverPS3_Context *)device->context;14051406Uint8 effects[] = {14070x0, // Report Id1408k_EPS3SixaxisCommandSetLEDs, // 1 = Set LEDs14090x00, 0x00, 0x00, // padding14100x00, 0x00, 0x00, 0x00 // LED #4, LED #3, LED #2, LED #1 (0 = Off, 1 = On, 2 = Flashing)1411};14121413// Turn on LED light on DS3 Controller for relevant player (player_index 0 lights up LED #1, player_index 1 lights up LED #2, etc)1414if (ctx->player_index < 4) {1415effects[8 - ctx->player_index] = 1;1416}14171418return HIDAPI_DriverPS3SonySixaxis_SendJoystickEffect(device, ctx->joystick, effects, sizeof(effects));1419}14201421SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS3SonySixaxis = {1422SDL_HINT_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER,1423true,1424HIDAPI_DriverPS3SonySixaxis_RegisterHints,1425HIDAPI_DriverPS3SonySixaxis_UnregisterHints,1426HIDAPI_DriverPS3SonySixaxis_IsEnabled,1427HIDAPI_DriverPS3SonySixaxis_IsSupportedDevice,1428HIDAPI_DriverPS3SonySixaxis_InitDevice,1429HIDAPI_DriverPS3SonySixaxis_GetDevicePlayerIndex,1430HIDAPI_DriverPS3SonySixaxis_SetDevicePlayerIndex,1431HIDAPI_DriverPS3SonySixaxis_UpdateDevice,1432HIDAPI_DriverPS3SonySixaxis_OpenJoystick,1433HIDAPI_DriverPS3SonySixaxis_RumbleJoystick,1434HIDAPI_DriverPS3SonySixaxis_RumbleJoystickTriggers,1435HIDAPI_DriverPS3SonySixaxis_GetJoystickCapabilities,1436HIDAPI_DriverPS3SonySixaxis_SetJoystickLED,1437HIDAPI_DriverPS3SonySixaxis_SendJoystickEffect,1438HIDAPI_DriverPS3SonySixaxis_SetJoystickSensorsEnabled,1439HIDAPI_DriverPS3SonySixaxis_CloseJoystick,1440HIDAPI_DriverPS3SonySixaxis_FreeDevice,1441};14421443#endif // SDL_JOYSTICK_HIDAPI_PS314441445#endif // SDL_JOYSTICK_HIDAPI144614471448