From b24f252850f0a9160830d3b39fecf2cf781ffc9d Mon Sep 17 00:00:00 2001 From: Luke A Date: Sat, 20 Jun 2026 16:52:04 -0400 Subject: [PATCH 01/11] Moving gamepad USB hosts to their own folder --- CMakeLists.txt | 2 + headers/addons/gamepad_usb_host_listener.h | 155 +--- headers/gphost.h | 35 + headers/hosts/DrivingForceHost.h | 0 headers/hosts/DualsensePS5Host.h | 63 ++ headers/hosts/DualshockPS4Host.h | 36 + headers/hosts/GoogleStadiaHost.h | 48 ++ headers/hosts/SwitchProHost.h | 20 + headers/hosts/Ultrastik360Host.h | 38 + headers/hosts/Xbox360Host.h | 0 headers/hosts/XboxOneHost.h | 0 src/addons/gamepad_usb_host.cpp | 12 +- src/addons/gamepad_usb_host_listener.cpp | 918 +++------------------ src/hosts/DrivingForceHost.cpp | 48 ++ src/hosts/DualsensePS5Host.cpp | 69 ++ src/hosts/DualshockPS4Host.cpp | 206 +++++ src/hosts/GoogleStadioHost.cpp | 40 + src/hosts/SwitchProHost.cpp | 214 +++++ src/hosts/Ultrastik360Host.cpp | 21 + src/hosts/Xbox360Host.cpp | 141 ++++ 20 files changed, 1105 insertions(+), 961 deletions(-) create mode 100644 headers/gphost.h create mode 100644 headers/hosts/DrivingForceHost.h create mode 100644 headers/hosts/DualsensePS5Host.h create mode 100644 headers/hosts/DualshockPS4Host.h create mode 100644 headers/hosts/GoogleStadiaHost.h create mode 100644 headers/hosts/SwitchProHost.h create mode 100644 headers/hosts/Ultrastik360Host.h create mode 100644 headers/hosts/Xbox360Host.h create mode 100644 headers/hosts/XboxOneHost.h create mode 100644 src/hosts/DrivingForceHost.cpp create mode 100644 src/hosts/DualsensePS5Host.cpp create mode 100644 src/hosts/DualshockPS4Host.cpp create mode 100644 src/hosts/GoogleStadioHost.cpp create mode 100644 src/hosts/SwitchProHost.cpp create mode 100644 src/hosts/Ultrastik360Host.cpp create mode 100644 src/hosts/Xbox360Host.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 38e6a896e7..c1cd4a9bba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,6 +304,7 @@ src/animationstation/effects/customthemepressed.cpp src/animationstation/effects/rainbow.cpp src/animationstation/effects/staticcolor.cpp src/animationstation/effects/statictheme.cpp +src/hosts/DualshockPS4Host.cpp ${PROTO_OUTPUT_DIR}/enums.pb.c ${PROTO_OUTPUT_DIR}/config.pb.c ) @@ -357,6 +358,7 @@ headers/display/ui/elements headers/display/ui/screens headers/animationstation headers/animationstation/effects +headers/hosts configs/${GP2040_BOARDCONFIG} ${PROTO_OUTPUT_DIR} ${CMAKE_BINARY_DIR}/headers diff --git a/headers/addons/gamepad_usb_host_listener.h b/headers/addons/gamepad_usb_host_listener.h index ad77db402b..84e294c95f 100644 --- a/headers/addons/gamepad_usb_host_listener.h +++ b/headers/addons/gamepad_usb_host_listener.h @@ -7,119 +7,16 @@ #include "drivers/ps4/PS4Descriptors.h" #include "drivers/switchpro/SwitchProDescriptors.h" +#include "gphost.h" + +// Move to individuals #define GAMEPAD_HOST_DEBUG false + +// Move to DS4 and Switch Pro #define GAMEPAD_HOST_USE_FEATURES true const uint32_t GAMEPAD_HOST_POLL_INTERVAL_MS = 3; -// Google Stadia controller report struct -typedef struct TU_ATTR_PACKED -{ - uint8_t reportId; // Report ID = 0x03 (3) - // Collection: CA:GamePad - int8_t GD_GamePadHatSwitch : 4; // Usage 0x00010039: Hat switch, Value = to 7, Physical = Value x 45 in degrees - int8_t : 1; // Pad - int8_t : 1; // Pad - int8_t : 1; // Pad - int8_t : 1; // Pad - uint8_t BTN_GamePadButton18 : 1; // Usage 0x00090012: Button 18, Value = 0 to 1 :: Capture button - uint8_t BTN_GamePadButton17 : 1; // Usage 0x00090011: Button 17, Value = 0 to 1 :: Google assistant - uint8_t BTN_GamePadButton20 : 1; // Usage 0x00090014: Button 20, Value = 0 to 1 :: L2 - uint8_t BTN_GamePadButton19 : 1; // Usage 0x00090013: Button 19, Value = 0 to 1 :: R2 - uint8_t BTN_GamePadButton13 : 1; // Usage 0x0009000D: Button 13, Value = 0 to 1 :: Stadia button - uint8_t BTN_GamePadButton12 : 1; // Usage 0x0009000C: Button 12, Value = 0 to 1 :: menu button - uint8_t BTN_GamePadButton11 : 1; // Usage 0x0009000B: Button 11, Value = 0 to 1 :: options - uint8_t BTN_GamePadButton15 : 1; // Usage 0x0009000F: Button 15, Value = 0 to 1 :: R3 - uint8_t BTN_GamePadButton14 : 1; // Usage 0x0009000E: Button 14, Value = 0 to 1 :: L3 - uint8_t BTN_GamePadButton8 : 1; // Usage 0x00090008: Button 8, Value = 0 to 1 :: R1 - uint8_t BTN_GamePadButton7 : 1; // Usage 0x00090007: Button 7, Value = 0 to 1 :: L1 - uint8_t BTN_GamePadButton5 : 1; // Usage 0x00090005: Button 5, Value = 0 to 1 :: Y button - uint8_t BTN_GamePadButton4 : 1; // Usage 0x00090004: Button 4, Value = 0 to 1 :: X button - uint8_t BTN_GamePadButton2 : 1; // Usage 0x00090002: Button 2 Secondary, Value = 0 to 1 :: B button - uint8_t BTN_GamePadButton1 : 1; // Usage 0x00090001: Button 1 Primary/trigger, Value = 0 to 1 :: A button - uint8_t : 1; // Pad - // Collection: CA:GamePad CP:Pointer - uint8_t GD_GamePadPointerX; // Usage 0x00010030: X, Value = 1 to 255 - uint8_t GD_GamePadPointerY; // Usage 0x00010031: Y, Value = 1 to 255 - uint8_t GD_GamePadPointerZ; // Usage 0x00010032: Z, Value = 1 to 255 - uint8_t GD_GamePadPointerRz; // Usage 0x00010035: Rz, Value = 1 to 255 - // Collection: CA:GamePad - uint8_t SIM_GamePadBrake; // Usage 0x000200C5: Brake, Value = 0 to 255 - uint8_t SIM_GamePadAccelerator; // Usage 0x000200C4: Accelerator, Value = 0 to 255 - -} google_stadia_report_t; - -// Ultrastik 360 -typedef struct TU_ATTR_PACKED -{ - uint8_t GD_GamePadPointerX; - uint8_t GD_GamePadPointerY; - - struct { - uint8_t BTN_GamePadButton1 : 1; - uint8_t BTN_GamePadButton2 : 1; - uint8_t BTN_GamePadButton3 : 1; - uint8_t BTN_GamePadButton4 : 1; - uint8_t BTN_GamePadButton5 : 1; - uint8_t BTN_GamePadButton6 : 1; - uint8_t BTN_GamePadButton7 : 1; - uint8_t BTN_GamePadButton8 : 1; - uint8_t BTN_GamePadButton9 : 1; - uint8_t BTN_GamePadButton10 : 1; - uint8_t BTN_GamePadButton11 : 1; - uint8_t BTN_GamePadButton12 : 1; - uint8_t BTN_GamePadButton13 : 1; - uint8_t BTN_GamePadButton14 : 1; - uint8_t BTN_GamePadButton15 : 1; - uint8_t padding : 1; - }; - -} ultrastik360_t; - -typedef struct __attribute__((packed)) { - uint8_t reportID; - uint8_t leftStickX; - uint8_t leftStickY; - uint8_t rightStickX; - uint8_t rightStickY; - uint8_t leftTrigger; - uint8_t rightTrigger; - - // 8 bit report counter. - uint8_t reportCounter; - - // 4 bits for the d-pad. - uint8_t dpad : 4; - - // 14 bits for buttons. - uint16_t buttonWest : 1; - uint16_t buttonSouth : 1; - uint16_t buttonEast : 1; - uint16_t buttonNorth : 1; - uint16_t buttonL1 : 1; - uint16_t buttonR1 : 1; - uint16_t buttonL2 : 1; - uint16_t buttonR2 : 1; - uint16_t buttonSelect : 1; - uint16_t buttonStart : 1; - uint16_t buttonL3 : 1; - uint16_t buttonR3 : 1; - uint16_t buttonHome : 1; - uint16_t buttonTouchpad : 1; - uint16_t buttonMicMute : 1; - - uint8_t miscData[54]; -} DSReport; - -typedef struct __attribute__((packed)) { - uint8_t command; - uint8_t counter; - uint8_t rumble_l[4]; - uint8_t rumble_r[4]; - uint8_t subcommand; - uint8_t subcommand_args[3]; -} SwitchProHostReport; - // Add other controller structs here class GamepadUSBHostListener : public USBListener { public:// USB Listener Features @@ -131,36 +28,23 @@ class GamepadUSBHostListener : public USBListener { virtual void report_sent(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) {} virtual void set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len); virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len); - void process(); + void gamepadFeatureUpdate(); private: - bool awaiting_cb = false; - bool host_get_report(uint8_t report_id, void* report, uint16_t len); - bool host_set_report(uint8_t report_id, void* report, uint16_t len); - - GamepadState _controller_host_state; - bool _controller_host_analog = true; - bool _controller_host_enabled; - uint8_t _controller_dev_addr = 0; - uint8_t _controller_instance = 0; - uint8_t _controller_type = 0; // 0 = HID, 1 = Xbox 360, 2 = Xbox One (see xinput_type_t) + bool _controller_host_mounted; + uint8_t _controller_dev_addr; + uint8_t _controller_instance; + GPHost * _controller_host; + uint32_t _next_update; + + /* + //GamepadState _controller_host_state; + //bool _controller_host_analog = true; + + //uint8_t _controller_type = 0; // 0 = HID, 1 = Xbox 360, 2 = Xbox One (see xinput_type_t) uint32_t _next_update = 0; // reliable update (e.g for rumble) called every GAMEPAD_HOST_POLL_INTERVAL_MS ms - void update_ctrlr(); - void process_ctrlr_report(uint8_t dev_addr, uint8_t const* report, uint16_t len); - - // Controller report processor functions - bool isDS4Identified = false; - bool hasDS4DefReport = false; - // ds4 initialization step, if needed - void init_ds4(const uint8_t* descReport, uint16_t descLen); - // general ds4 setup - void setup_ds4(); - // update ds4 output reporting - void update_ds4(); - // handle ds4 input reporting - void process_ds4(uint8_t const* report, uint16_t len); - PS4ControllerConfig ds4Config; - uint8_t report_buffer[PS4_ENDPOINT_SIZE]; + //void update_ctrlr(); + //void process_ctrlr_report(uint8_t dev_addr, uint8_t const* report, uint16_t len); void process_ds(uint8_t const* report, uint16_t len); @@ -200,6 +84,7 @@ class GamepadUSBHostListener : public USBListener { bool isDFInit = false; void setup_df_wheel(); void process_dfgt(uint8_t const* report, uint16_t len); + */ }; #endif diff --git a/headers/gphost.h b/headers/gphost.h new file mode 100644 index 0000000000..cec76f07d0 --- /dev/null +++ b/headers/gphost.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Copyright (c) 2026 OpenStickCommunity (gp2040-ce.info) + */ + +#ifndef _GPHOST_H_ +#define _GPHOST_H_ + +#include + +#include "gamepad/GamepadState.h" + +// Forward declare gamepad +class Gamepad; + +// +// GP2040-CE USB Device Class Driver +// +class GPHost { +public: + virtual ~GPHost() { } + virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) = 0; + virtual void process(uint8_t const* report, uint16_t len) = 0; + virtual void update() = 0; + virtual void gamepad(Gamepad * gamepad) = 0; + virtual void shutdown() = 0; +protected: + GamepadState _controller_host_state; + uint8_t _dev_addr; + uint8_t _instance; + uint16_t _vendor_id; + uint16_t _product_id; +}; + +#endif diff --git a/headers/hosts/DrivingForceHost.h b/headers/hosts/DrivingForceHost.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/headers/hosts/DualsensePS5Host.h b/headers/hosts/DualsensePS5Host.h new file mode 100644 index 0000000000..4b575327f7 --- /dev/null +++ b/headers/hosts/DualsensePS5Host.h @@ -0,0 +1,63 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Copyright (c) 2026 OpenStickCommunity (gp2040-ce.info) + */ + +#ifndef _DUALSENSEPS5HOST_H_ +#define _DUALSENSEPS5HOST_H_ + +#include + +#include "drivers/ps4/PS4Descriptors.h" +#include "drivers/ps4/PS4Driver.h" + +typedef struct __attribute__((packed)) { + uint8_t reportID; + uint8_t leftStickX; + uint8_t leftStickY; + uint8_t rightStickX; + uint8_t rightStickY; + uint8_t leftTrigger; + uint8_t rightTrigger; + + // 8 bit report counter. + uint8_t reportCounter; + + // 4 bits for the d-pad. + uint8_t dpad : 4; + + // 14 bits for buttons. + uint16_t buttonWest : 1; + uint16_t buttonSouth : 1; + uint16_t buttonEast : 1; + uint16_t buttonNorth : 1; + uint16_t buttonL1 : 1; + uint16_t buttonR1 : 1; + uint16_t buttonL2 : 1; + uint16_t buttonR2 : 1; + uint16_t buttonSelect : 1; + uint16_t buttonStart : 1; + uint16_t buttonL3 : 1; + uint16_t buttonR3 : 1; + uint16_t buttonHome : 1; + uint16_t buttonTouchpad : 1; + uint16_t buttonMicMute : 1; + + uint8_t miscData[54]; +} DSReport; + +// +// Dualsense PS5 Controller Host +// +class DualsenseHost : public GPHost { +public: + virtual bool match(uint8_t vendor_id, uint8_t product_id); + virtual void initialize(); + virtual void gamepad(Gamepad * gamepad); + virtual void process(uint8_t const* report, uint16_t len); // all these? + virtual void shutdown(); +private: + bool requiresInit; +}; + +#endif // _DUALSENSEHOST_H_ diff --git a/headers/hosts/DualshockPS4Host.h b/headers/hosts/DualshockPS4Host.h new file mode 100644 index 0000000000..1732c89c41 --- /dev/null +++ b/headers/hosts/DualshockPS4Host.h @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Copyright (c) 2026 OpenStickCommunity (gp2040-ce.info) + */ + +#ifndef _DUALSHOCKPS4HOST_H_ +#define _DUALSHOCKPS4HOST_H_ + +#include + +#include "drivers/ps4/PS4Descriptors.h" +#include "drivers/ps4/PS4Driver.h" + +// +// GP2040-CE USB Host Driver +// +class DualshockPS4Host : public GPHost { +public: + static bool match(uint16_t vendor_id, uint16_t product_id); + virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); + virtual void gamepad(Gamepad * gamepad); + virtual void process(uint8_t const* report, uint16_t len); + virtual void update(); + virtual void shutdown(); +private: + // Controller report processor functions + PS4Report prevReport; + bool validDS4HID; + PS4ControllerConfig ds4Config; + uint8_t report_buffer[PS4_ENDPOINT_SIZE]; + uint32_t map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max); + bool diff_than_2(uint8_t x, uint8_t y); + bool diff_report(PS4Report const* rpt1, PS4Report const* rpt2); +}; + +#endif diff --git a/headers/hosts/GoogleStadiaHost.h b/headers/hosts/GoogleStadiaHost.h new file mode 100644 index 0000000000..90ed46d72e --- /dev/null +++ b/headers/hosts/GoogleStadiaHost.h @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Copyright (c) 2026 OpenStickCommunity (gp2040-ce.info) + */ + +#ifndef _GOOGLESTADIAHOST_H_ +#define _GOOGLESTADIAHOST_H_ + +#include + +// Google Stadia controller report struct +typedef struct TU_ATTR_PACKED +{ + uint8_t reportId; // Report ID = 0x03 (3) + // Collection: CA:GamePad + int8_t GD_GamePadHatSwitch : 4; // Usage 0x00010039: Hat switch, Value = to 7, Physical = Value x 45 in degrees + int8_t : 1; // Pad + int8_t : 1; // Pad + int8_t : 1; // Pad + int8_t : 1; // Pad + uint8_t BTN_GamePadButton18 : 1; // Usage 0x00090012: Button 18, Value = 0 to 1 :: Capture button + uint8_t BTN_GamePadButton17 : 1; // Usage 0x00090011: Button 17, Value = 0 to 1 :: Google assistant + uint8_t BTN_GamePadButton20 : 1; // Usage 0x00090014: Button 20, Value = 0 to 1 :: L2 + uint8_t BTN_GamePadButton19 : 1; // Usage 0x00090013: Button 19, Value = 0 to 1 :: R2 + uint8_t BTN_GamePadButton13 : 1; // Usage 0x0009000D: Button 13, Value = 0 to 1 :: Stadia button + uint8_t BTN_GamePadButton12 : 1; // Usage 0x0009000C: Button 12, Value = 0 to 1 :: menu button + uint8_t BTN_GamePadButton11 : 1; // Usage 0x0009000B: Button 11, Value = 0 to 1 :: options + uint8_t BTN_GamePadButton15 : 1; // Usage 0x0009000F: Button 15, Value = 0 to 1 :: R3 + uint8_t BTN_GamePadButton14 : 1; // Usage 0x0009000E: Button 14, Value = 0 to 1 :: L3 + uint8_t BTN_GamePadButton8 : 1; // Usage 0x00090008: Button 8, Value = 0 to 1 :: R1 + uint8_t BTN_GamePadButton7 : 1; // Usage 0x00090007: Button 7, Value = 0 to 1 :: L1 + uint8_t BTN_GamePadButton5 : 1; // Usage 0x00090005: Button 5, Value = 0 to 1 :: Y button + uint8_t BTN_GamePadButton4 : 1; // Usage 0x00090004: Button 4, Value = 0 to 1 :: X button + uint8_t BTN_GamePadButton2 : 1; // Usage 0x00090002: Button 2 Secondary, Value = 0 to 1 :: B button + uint8_t BTN_GamePadButton1 : 1; // Usage 0x00090001: Button 1 Primary/trigger, Value = 0 to 1 :: A button + uint8_t : 1; // Pad + // Collection: CA:GamePad CP:Pointer + uint8_t GD_GamePadPointerX; // Usage 0x00010030: X, Value = 1 to 255 + uint8_t GD_GamePadPointerY; // Usage 0x00010031: Y, Value = 1 to 255 + uint8_t GD_GamePadPointerZ; // Usage 0x00010032: Z, Value = 1 to 255 + uint8_t GD_GamePadPointerRz; // Usage 0x00010035: Rz, Value = 1 to 255 + // Collection: CA:GamePad + uint8_t SIM_GamePadBrake; // Usage 0x000200C5: Brake, Value = 0 to 255 + uint8_t SIM_GamePadAccelerator; // Usage 0x000200C4: Accelerator, Value = 0 to 255 + +} google_stadia_report_t; + +#endif diff --git a/headers/hosts/SwitchProHost.h b/headers/hosts/SwitchProHost.h new file mode 100644 index 0000000000..6cbb161aab --- /dev/null +++ b/headers/hosts/SwitchProHost.h @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Copyright (c) 2026 OpenStickCommunity (gp2040-ce.info) + */ + +#ifndef _SWITCHPROHOST_H_ +#define _SWITCHPROHOST_H_ + +#include + +typedef struct __attribute__((packed)) { + uint8_t command; + uint8_t counter; + uint8_t rumble_l[4]; + uint8_t rumble_r[4]; + uint8_t subcommand; + uint8_t subcommand_args[3]; +} SwitchProHostReport; + +#endif diff --git a/headers/hosts/Ultrastik360Host.h b/headers/hosts/Ultrastik360Host.h new file mode 100644 index 0000000000..8020df79c3 --- /dev/null +++ b/headers/hosts/Ultrastik360Host.h @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Copyright (c) 2026 OpenStickCommunity (gp2040-ce.info) + */ + +#ifndef _ULTRASTIK360HOST_H_ +#define _ULTRASTIK360PROHOST_H_ + +#include + +// Ultrastik 360 +typedef struct TU_ATTR_PACKED +{ + uint8_t GD_GamePadPointerX; + uint8_t GD_GamePadPointerY; + + struct { + uint8_t BTN_GamePadButton1 : 1; + uint8_t BTN_GamePadButton2 : 1; + uint8_t BTN_GamePadButton3 : 1; + uint8_t BTN_GamePadButton4 : 1; + uint8_t BTN_GamePadButton5 : 1; + uint8_t BTN_GamePadButton6 : 1; + uint8_t BTN_GamePadButton7 : 1; + uint8_t BTN_GamePadButton8 : 1; + uint8_t BTN_GamePadButton9 : 1; + uint8_t BTN_GamePadButton10 : 1; + uint8_t BTN_GamePadButton11 : 1; + uint8_t BTN_GamePadButton12 : 1; + uint8_t BTN_GamePadButton13 : 1; + uint8_t BTN_GamePadButton14 : 1; + uint8_t BTN_GamePadButton15 : 1; + uint8_t padding : 1; + }; + +} ultrastik360_t; + +#endif diff --git a/headers/hosts/Xbox360Host.h b/headers/hosts/Xbox360Host.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/headers/hosts/XboxOneHost.h b/headers/hosts/XboxOneHost.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/addons/gamepad_usb_host.cpp b/src/addons/gamepad_usb_host.cpp index 3138093141..d6f9bf290f 100644 --- a/src/addons/gamepad_usb_host.cpp +++ b/src/addons/gamepad_usb_host.cpp @@ -6,20 +6,16 @@ #include "peripheralmanager.h" #include "class/hid/hid_host.h" -bool GamepadUSBHostAddon::available() -{ +bool GamepadUSBHostAddon::available() { const GamepadUSBHostOptions& gamepadUSBHostOptions = Storage::getInstance().getAddonOptions().gamepadUSBHostOptions; return gamepadUSBHostOptions.enabled && PeripheralManager::getInstance().isUSBEnabled(0); } -void GamepadUSBHostAddon::setup() -{ - //stdio_init_all(); - //printf("setup function\n"); +void GamepadUSBHostAddon::setup() { listener = new GamepadUSBHostListener(); - ((GamepadUSBHostListener*)listener)->setup(); + listener->setup(); } void GamepadUSBHostAddon::preprocess() { - ((GamepadUSBHostListener*)listener)->process(); + ((GamepadUSBHostListener*)listener)->gamepadFeatureUpdate(); } \ No newline at end of file diff --git a/src/addons/gamepad_usb_host_listener.cpp b/src/addons/gamepad_usb_host_listener.cpp index 657bc7aa17..da871fdd8f 100644 --- a/src/addons/gamepad_usb_host_listener.cpp +++ b/src/addons/gamepad_usb_host_listener.cpp @@ -3,198 +3,138 @@ #include "class/hid/hid.h" #include "class/hid/hid_host.h" -#include "drivers/ps3/PS3Descriptors.h" -#include "drivers/ps4/PS4Descriptors.h" -#include "drivers/ps4/PS4Driver.h" -#include "drivers/xinput/XInputDescriptors.h" -#include "drivers/xbone/XBOneDescriptors.h" -#include "drivers/switchpro/SwitchProDescriptors.h" -#include "drivers/shared/xinput_host.h" +//#include "hosts/DualsensePS5Host.h" +#include "hosts/DualshockPS4Host.h" + +/* +#include "hosts/DrivingForceHost.h" +#include "hosts/GoogleStadiaHost.h" +#include "hosts/SwitchProHost.h" +#include "hosts/UltraStik360Host.h" +#include "hosts/Xbox360Host.h" +#include "hosts/XboxOneHost.h" +*/ void GamepadUSBHostListener::setup() { - _controller_host_enabled = false; -#if GAMEPAD_HOST_DEBUG - stdio_init_all(); -#endif + _controller_host_mounted = false; + _controller_host = nullptr; + _controller_dev_addr = 0; + _controller_instance = 0; + _next_update = 0; } -void GamepadUSBHostListener::process() { - Gamepad *gamepad = Storage::getInstance().GetGamepad(); - gamepad->hasAnalogTriggers = _controller_host_analog; - gamepad->hasLeftAnalogStick = _controller_host_analog; - gamepad->hasRightAnalogStick = _controller_host_analog; - gamepad->state.dpad |= _controller_host_state.dpad; - gamepad->state.buttons |= _controller_host_state.buttons; - gamepad->state.lx = _controller_host_state.lx; - gamepad->state.ly = _controller_host_state.ly; - gamepad->state.rx = _controller_host_state.rx; - gamepad->state.ry = _controller_host_state.ry; - gamepad->state.rt = _controller_host_state.rt; - gamepad->state.lt = _controller_host_state.lt; +void GamepadUSBHostListener::gamepadFeatureUpdate() { + if ( _controller_host_mounted == false ) return; - if (_controller_host_enabled && getMillis() > _next_update) { - update_ctrlr(); + // Controller host updates (rumble, LEDs, etc. outside of gamepad input reports) + if (getMillis() > _next_update) { + _controller_host->update(); _next_update = getMillis() + GAMEPAD_HOST_POLL_INTERVAL_MS; } + + // Controller host driver changes gamepad + Gamepad *gamepad = Storage::getInstance().GetGamepad(); + _controller_host->gamepad(gamepad); } void GamepadUSBHostListener::mount(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) { - uint16_t vid = 0; - uint16_t pid = 0; - tuh_vid_pid_get(dev_addr, &vid, &pid); - - if (_controller_host_enabled - && vid != 0 && pid != 0 - && vid == controller_vid - && pid == controller_pid - && _controller_type == 1) { - // received XInput again... -#ifdef GAMEPAD_HOST_DEBUG - printf("Ignoring mount twice (XInput -> HID) on VID_%04x PID_%04x\n", vid, pid); -#endif + // Only host one controller at a time + if ( _controller_host_mounted == true ) { return; } - _controller_host_enabled = true; - _controller_dev_addr = dev_addr; - _controller_instance = instance; - _controller_type = xinput_type_t::UNKNOWN; // HID controller - controller_vid = vid; - controller_pid = pid; - -#if GAMEPAD_HOST_DEBUG - printf("Mount: VID_%04x PID_%04x\n", controller_vid, controller_pid); -#endif - - uint16_t joystick_mid = GAMEPAD_JOYSTICK_MID; - _controller_host_state.buttons = 0; - _controller_host_state.dpad = 0; - _controller_host_state.lx = joystick_mid; - _controller_host_state.ly = joystick_mid; - _controller_host_state.rx = joystick_mid; - _controller_host_state.ry = joystick_mid; + uint16_t vid = 0, pid = 0; + tuh_vid_pid_get(dev_addr, &vid, &pid); - switch(controller_pid) - { - /* PS4/5 */ - // these require initialization - case PS4_PRODUCT_ID: // Razer Panthera - case 0x00EE: // Hori Minipad - case PS4_WHEEL_PRODUCT_ID: // G29 - case 0xB67B: // T-Flight - init_ds4(desc_report, desc_len); - break; - // while these do not - case DS4_ORG_PRODUCT_ID: // Sony Dualshock 4 controller - case DS4_PRODUCT_ID: // Sony Dualshock 4 controller - isDS4Identified = true; - setup_ds4(); - break; - case 0x0CE6: // DualSense - break; + // TODO: Make a list of static functions + pointers + // Match is a static function, setup and init happens after we've established something + _controller_host = nullptr; + if ( DualshockPS4Host::match(vid, pid) ) { + _controller_host = (GPHost*)(new DualshockPS4Host()); + }/* else if ( DualsensePS5Host::match(vid, pid) ) { + _controller_host = new DualsensePS5Host(); + }else if ( GoogleStadiaHost::match(vid, pid) ) { + _controller_host = new GoogleStadiaHost(); + } else if ( SwitchProHost::match(vid, pid) ) { + _controller_host = new SwitchProHost(); + } else if ( DrivingForceHost::match(vid, pid) ) { + _controller_host = new DrivingForceHost(); + } else if ( Ultrastik360Host::match(vid, pid) ) { + _controller_host = new Ultrastik360Host(); + } else { + // Better way to determine if its a generic HID? - case 0xC294: // Driving Force or similar - isDFInit = false; - setup_df_wheel(); - break; - case 0xC29A: - isDFInit = true; - break; + //if ( GenericHIDHost::match(vid, pid) ) { // do we want this one? + //controllerHost = new GenericHIDHost(); + }*/ - case SWITCH_PRO_PRODUCT_ID: // Nintendo Switch Pro controller - switchProFinished = false; - switchReportCounter = 0; - switchProState = SwitchOutputSubtypes::IDENTIFY; - lastSwitchLed = 0; - // wait for init from the controller, don't setup immediately - break; - - /* Other */ - // these types do not have an identification step, at least for PS4 - case 0x9400: // Google Stadia controller - case 0x0510: // pre-2015 Ultrakstik 360 - case 0x0511: // Ultrakstik 360 - default: - break; + if ( _controller_host != nullptr ) { + _controller_host_mounted = true; + _controller_dev_addr = dev_addr; + _controller_instance = instance; + _controller_host->initialize(dev_addr, instance, vid, pid, desc_report, desc_len); + } else { + // send message to the main screen that we have an unknown controller + + // if auto-detect is enabled, let's try to determine controller type by sending some special packets + // Order: ps4 + // Order: ps5 + // Order: switch pro + // Order: generic HID } } void GamepadUSBHostListener::xmount(uint8_t dev_addr, uint8_t instance, uint8_t controllerType, uint8_t subtype) { - _controller_host_enabled = true; - _controller_dev_addr = dev_addr; - _controller_instance = instance; - _controller_type = controllerType; - tuh_vid_pid_get(dev_addr, &controller_vid, &controller_pid); + // Only host one controller at a time + if ( _controller_host_mounted == true ) { + return; + } -#if GAMEPAD_HOST_DEBUG - printf("XMount: VID_%04x PID_%04x Type:%d Subtype:%d\n", controller_vid, controller_pid, controllerType, subtype); -#endif + uint16_t vid, pid; + tuh_vid_pid_get(dev_addr, &vid, &pid); + + // Match is a static function, setup and init happens after we've established something + _controller_host = nullptr; + /*if ( Xbox360Host::match(vid, pid) && controllerType == 1 ) { + _controller_host = new Xbox360Host(); + } else if ( XboxOneHost::match(vid, pid && controllerType == 2 ) { + _controller_host = new XboxOneHost(); + }*/ - uint16_t joystick_mid = GAMEPAD_JOYSTICK_MID; - _controller_host_state.buttons = 0; - _controller_host_state.dpad = 0; - _controller_host_state.lx = joystick_mid; - _controller_host_state.ly = joystick_mid; - _controller_host_state.rx = joystick_mid; - _controller_host_state.ry = joystick_mid; + if ( _controller_host != nullptr ) { + _controller_host_mounted = true; + _controller_dev_addr = dev_addr; + _controller_instance = instance; + _controller_host->initialize(dev_addr, instance, vid, pid, nullptr, 0); + } else { + // send message to the main screen that we have an unknown controller - if (controllerType != xinput_type_t::UNKNOWN) { - switch (controllerType) { - case xinput_type_t::XBOX360: - break; // no init needed - case xinput_type_t::XBOXONE: - // init not implemented - break; - default: - break; - } - setup_xinput(dev_addr, instance); + // if auto-magic + // check for xbox 360 markers + // check for xbox one markers } } void GamepadUSBHostListener::unmount(uint8_t dev_addr) { -#if GAMEPAD_HOST_DEBUG - printf("Unmount: %x\n", dev_addr); -#endif - _controller_host_enabled = false; - controller_pid = 0x00; - controller_vid = 0x00; - _controller_dev_addr = 0; - _controller_instance = 0; - _controller_type = 0; - isDS4Identified = false; - hasDS4DefReport = false; - switchProFinished = false; - switchProState = SwitchOutputSubtypes::IDENTIFY; - switchReportCounter = 0; - lastSwitchLed = 0; + if ( _controller_host_mounted == true ) { + _controller_host->shutdown(); + delete _controller_host; + _controller_host = nullptr; + _controller_host_mounted = false; + } } void GamepadUSBHostListener::report_received(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) { // if a hid device hasn't been mounted - if ( _controller_host_enabled == false ) return; - - // handle xinput - switch (_controller_type) { - case xinput_type_t::XBOX360: - process_xbox360(report, len); - return; - case xinput_type_t::XBOXONE: - // not implemented - return; - default: - break; - } + if ( _controller_host_mounted == false ) return; - // Interface protocol (hid_interface_protocol_enum_t) - uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); - - // stop execution if a keyboard or mouse is mounted - if ( itf_protocol == HID_ITF_PROTOCOL_KEYBOARD ) return; - - process_ctrlr_report(dev_addr, report, len); + if ( _controller_dev_addr == dev_addr && + _controller_instance == instance ) { + _controller_host->process(report, len); + } } +/* void GamepadUSBHostListener::process_ctrlr_report(uint8_t dev_addr, uint8_t const* report, uint16_t len) { #if GAMEPAD_HOST_DEBUG //printf("\033[1;0H\nHost (%d):\n", len); @@ -276,675 +216,17 @@ void GamepadUSBHostListener::update_ctrlr() { } } + bool GamepadUSBHostListener::host_get_report(uint8_t report_id, void* report, uint16_t len) { - awaiting_cb = true; return tuh_hid_get_report(_controller_dev_addr, _controller_instance, report_id, HID_REPORT_TYPE_FEATURE, report, len); } bool GamepadUSBHostListener::host_set_report(uint8_t report_id, void* report, uint16_t len) { - awaiting_cb = true; return tuh_hid_set_report(_controller_dev_addr, _controller_instance, report_id, HID_REPORT_TYPE_FEATURE, report, len); } - +*/ void GamepadUSBHostListener::set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) { - awaiting_cb = false; } void GamepadUSBHostListener::get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) { -#if GAMEPAD_HOST_DEBUG - //printf("get_report_complete Report ID: %02x\n", report_id); -#endif - if (!isDS4Identified) { - switch (report_id) { - case PS4AuthReport::PS4_DEFINITION: - setup_ds4(); - break; - default: - break; - } - } - // - awaiting_cb = false; -} - -uint32_t GamepadUSBHostListener::map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max) { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} - - -//check if different than 2 -bool GamepadUSBHostListener::diff_than_2(uint8_t x, uint8_t y) { - return (x - y > 2) || (y - x > 2); -} - -// check if 2 reports are different enough -bool GamepadUSBHostListener::diff_report(PS4Report const* rpt1, PS4Report const* rpt2) { - bool result; - - // x, y, z, rz must different than 2 to be counted - result = diff_than_2(rpt1->leftStickX, rpt2->leftStickX) || diff_than_2(rpt1->leftStickY, rpt2->leftStickY) || - diff_than_2(rpt1->rightStickX, rpt2->rightStickX) || diff_than_2(rpt1->rightStickY, rpt2->rightStickY); - - // check the rest with mem compare - result |= memcmp(&rpt1->rightStickY + 1, &rpt2->rightStickY + 1, sizeof(PS4Report)-6); - - return result; -} - -void GamepadUSBHostListener::setup_ds4() { - if (hasDS4DefReport) { - // report came from the controller so copy the buffer - memcpy(&ds4Config, report_buffer+1, sizeof(PS4ControllerConfig)); - } - if ((ds4Config.hidUsage == 0x2721) || (ds4Config.hidUsage == 0x2127)) { - isDS4Identified = true; -#if GAMEPAD_HOST_DEBUG - //printf("PS4 controller details\n"); - //printf("----------------------\n"); - //printf("enableController: %d\n", ds4Config.features.enableController); - //printf("enableMotion: %d\n", ds4Config.features.enableMotion); - //printf("enableLED: %d\n", ds4Config.features.enableLED); - //printf("enableRumble: %d\n", ds4Config.features.enableRumble); - //printf("enableAnalog: %d\n", ds4Config.features.enableAnalog); - //printf("enableUnknown0: %d\n", ds4Config.features.enableUnknown0); - //printf("enableTouchpad: %d\n", ds4Config.features.enableTouchpad); - //printf("enableUnknown1: %d\n", ds4Config.features.enableUnknown1); -#endif - } -} - -void GamepadUSBHostListener::init_ds4(const uint8_t* descReport, uint16_t descLen) { - isDS4Identified = false; - - tuh_hid_report_info_t report_info[4]; - uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 4, descReport, descLen); - for(uint8_t i = 0; i < report_count; i++) { -#if GAMEPAD_HOST_DEBUG - //printf("Report: %02x, Usage: %04x, Usage Page: %04x\n", report_info[i].report_id, report_info[i].usage_page, report_info[i].usage); -#endif - if (report_info[i].report_id == PS4AuthReport::PS4_DEFINITION) { - // controller is some other type that's not a DS4, so parse the config - memset(report_buffer, 0, PS4_ENDPOINT_SIZE); - report_buffer[0] = PS4AuthReport::PS4_DEFINITION; - host_get_report(PS4AuthReport::PS4_DEFINITION, report_buffer, 48); - hasDS4DefReport = true; - break; - } - } - - if (!hasDS4DefReport) { - // no report found, DS4 or clone assume. use struct default data. - //isDS4Identified = true; - } -} - -void GamepadUSBHostListener::update_ds4() { -#if GAMEPAD_HOST_USE_FEATURES - Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); - PS4FeatureOutputReport controller_output; - - memset(&controller_output, 0, sizeof(controller_output)); - - controller_output.reportID = PS4AuthReport::PS4_SET_FEATURE_STATE; - - if (ds4Config.features.enableLED && gamepad->auxState.sensors.statusLight.enabled) { - controller_output.enableUpdateLED = gamepad->auxState.sensors.statusLight.enabled; - controller_output.ledRed = gamepad->auxState.sensors.statusLight.color.red; - controller_output.ledGreen = gamepad->auxState.sensors.statusLight.color.green; - controller_output.ledBlue = gamepad->auxState.sensors.statusLight.color.blue; - controller_output.ledBlinkOn = gamepad->auxState.playerID.ledBlinkOn; - controller_output.ledBlinkOff = gamepad->auxState.playerID.ledBlinkOff; - } - - if (ds4Config.features.enableRumble) { - gamepad->auxState.haptics.leftActuator.enabled = 1; - gamepad->auxState.haptics.rightActuator.enabled = 1; - controller_output.enableUpdateRumble = 1; - controller_output.rumbleLeft = gamepad->auxState.haptics.leftActuator.intensity; - controller_output.rumbleRight = gamepad->auxState.haptics.rightActuator.intensity; - } - - void * report = &controller_output; - uint16_t report_size = sizeof(controller_output)-1; - - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 5, (uint8_t*)report+1, report_size); -#endif -} - -void GamepadUSBHostListener::process_ds4(uint8_t const* report, uint16_t len) { - PS4Report controller_report; - - // previous report used to compare for changes - static PS4Report prev_report = { 0 }; - - uint8_t const report_id = report[0]; - - if (report_id == 1) { - memcpy(&controller_report, report, sizeof(controller_report)); - - if ( diff_report(&prev_report, &controller_report) ) { - _controller_host_state.lx = map(controller_report.leftStickX, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ly = map(controller_report.leftStickY, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.rx = map(controller_report.rightStickX,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ry = map(controller_report.rightStickY,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.lt = controller_report.leftTrigger; - _controller_host_state.rt = controller_report.rightTrigger; - _controller_host_analog = true; - - _controller_host_state.buttons = 0; - if (controller_report.buttonTouchpad) _controller_host_state.buttons |= GAMEPAD_MASK_A2; - if (controller_report.buttonSelect) _controller_host_state.buttons |= GAMEPAD_MASK_S1; - if (controller_report.buttonR3) _controller_host_state.buttons |= GAMEPAD_MASK_R3; - if (controller_report.buttonL3) _controller_host_state.buttons |= GAMEPAD_MASK_L3; - if (controller_report.buttonHome) _controller_host_state.buttons |= GAMEPAD_MASK_A1; - if (controller_report.buttonStart) _controller_host_state.buttons |= GAMEPAD_MASK_S2; - if (controller_report.buttonR1) _controller_host_state.buttons |= GAMEPAD_MASK_R1; - if (controller_report.buttonL1) _controller_host_state.buttons |= GAMEPAD_MASK_L1; - if (controller_report.buttonNorth) _controller_host_state.buttons |= GAMEPAD_MASK_B4; - if (controller_report.buttonEast) _controller_host_state.buttons |= GAMEPAD_MASK_B2; - if (controller_report.buttonSouth) _controller_host_state.buttons |= GAMEPAD_MASK_B1; - if (controller_report.buttonWest) _controller_host_state.buttons |= GAMEPAD_MASK_B3; - if (controller_report.buttonR2) _controller_host_state.buttons |= GAMEPAD_MASK_R2; - if (controller_report.buttonL2) _controller_host_state.buttons |= GAMEPAD_MASK_L2; - - _controller_host_state.dpad = 0; - if (controller_report.dpad == PS4_HAT_UP) _controller_host_state.dpad |= GAMEPAD_MASK_UP; - if (controller_report.dpad == PS4_HAT_UPRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_UP | GAMEPAD_MASK_RIGHT; - if (controller_report.dpad == PS4_HAT_RIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; - if (controller_report.dpad == PS4_HAT_DOWNRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT | GAMEPAD_MASK_DOWN; - if (controller_report.dpad == PS4_HAT_DOWN) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; - if (controller_report.dpad == PS4_HAT_DOWNLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN | GAMEPAD_MASK_LEFT; - if (controller_report.dpad == PS4_HAT_LEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; - if (controller_report.dpad == PS4_HAT_UPLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT | GAMEPAD_MASK_UP; - } - } - - prev_report = controller_report; -} - -void GamepadUSBHostListener::process_ds(uint8_t const* report, uint16_t len) { - DSReport controller_report; - - // previous report used to compare for changes - static DSReport prev_ds_report = { 0 }; - - uint8_t const report_id = report[0]; - - if (report_id == 1) { - memcpy(&controller_report, report, sizeof(controller_report)); - - if ( prev_ds_report.reportCounter != controller_report.reportCounter ) { - _controller_host_state.lx = map(controller_report.leftStickX, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ly = map(controller_report.leftStickY, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.rx = map(controller_report.rightStickX,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ry = map(controller_report.rightStickY,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.lt = controller_report.leftTrigger; - _controller_host_state.rt = controller_report.rightTrigger; - _controller_host_analog = true; - - _controller_host_state.buttons = 0; - if (controller_report.buttonTouchpad) _controller_host_state.buttons |= GAMEPAD_MASK_A2; - if (controller_report.buttonSelect) _controller_host_state.buttons |= GAMEPAD_MASK_S1; - if (controller_report.buttonR3) _controller_host_state.buttons |= GAMEPAD_MASK_R3; - if (controller_report.buttonL3) _controller_host_state.buttons |= GAMEPAD_MASK_L3; - if (controller_report.buttonHome) _controller_host_state.buttons |= GAMEPAD_MASK_A1; - if (controller_report.buttonStart) _controller_host_state.buttons |= GAMEPAD_MASK_S2; - if (controller_report.buttonR1) _controller_host_state.buttons |= GAMEPAD_MASK_R1; - if (controller_report.buttonL1) _controller_host_state.buttons |= GAMEPAD_MASK_L1; - if (controller_report.buttonNorth) _controller_host_state.buttons |= GAMEPAD_MASK_B4; - if (controller_report.buttonEast) _controller_host_state.buttons |= GAMEPAD_MASK_B2; - if (controller_report.buttonSouth) _controller_host_state.buttons |= GAMEPAD_MASK_B1; - if (controller_report.buttonWest) _controller_host_state.buttons |= GAMEPAD_MASK_B3; - if (controller_report.buttonR2) _controller_host_state.buttons |= GAMEPAD_MASK_R2; - if (controller_report.buttonL2) _controller_host_state.buttons |= GAMEPAD_MASK_L2; - - _controller_host_state.dpad = 0; - if (controller_report.dpad == PS4_HAT_UP) _controller_host_state.dpad |= GAMEPAD_MASK_UP; - if (controller_report.dpad == PS4_HAT_UPRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_UP | GAMEPAD_MASK_RIGHT; - if (controller_report.dpad == PS4_HAT_RIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; - if (controller_report.dpad == PS4_HAT_DOWNRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT | GAMEPAD_MASK_DOWN; - if (controller_report.dpad == PS4_HAT_DOWN) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; - if (controller_report.dpad == PS4_HAT_DOWNLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN | GAMEPAD_MASK_LEFT; - if (controller_report.dpad == PS4_HAT_LEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; - if (controller_report.dpad == PS4_HAT_UPLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT | GAMEPAD_MASK_UP; - } - } - - prev_ds_report = controller_report; -} - -// init phase -// https://github.com/wiredopposite/OGX-Mini/blob/ccccf6651b54bcca3c94b5d4e73e8f2796fc8c05/Firmware/RP2040/src/USBHost/HostDriver/SwitchPro/SwitchPro.cpp#L23 -void GamepadUSBHostListener::setup_switch_pro(uint8_t const *report, uint16_t len) { - SwitchProHostReport out_report{ - .rumble_l = {0x00, 0x01, 0x40, 0x40}, // default rumble states - .rumble_r = {0x00, 0x01, 0x40, 0x40}, - }; - uint8_t report_size = 10; // no subcommand - -#ifdef GAMEPAD_HOST_DEBUG - printf("Switch Pro init state: %d\n.", switchProState); -#endif - - switch (switchProState) - { - case SwitchOutputSubtypes::IDENTIFY: { - if (len < 10 - || report[0] != SwitchReportID::REPORT_USB_INPUT_81 - || report[1] != SwitchOutputSubtypes::IDENTIFY) { - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &SWITCH_INIT_REPORT, sizeof(SWITCH_INIT_REPORT)); - break; - } - - out_report.command = SwitchReportID::REPORT_CONFIGURATION; - out_report.counter = SwitchOutputSubtypes::HANDSHAKE; - get_next_switch_counter(); // skip counter - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &out_report, report_size); - // might turn up the home LED as well - switchProState = SwitchOutputSubtypes::DISABLE_USB_TIMEOUT; - break; - } - case SwitchOutputSubtypes::DISABLE_USB_TIMEOUT: { - if (len < 2 || - (report[0] != SwitchReportID::REPORT_USB_INPUT_81 - && report[0] != SwitchReportID::REPORT_OUTPUT_30)) { - // reset - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &SWITCH_INIT_REPORT, sizeof(SWITCH_INIT_REPORT)); - switchProState = SwitchOutputSubtypes::IDENTIFY; - break; - } - out_report.command = SwitchReportID::REPORT_CONFIGURATION; - out_report.counter = SwitchOutputSubtypes::DISABLE_USB_TIMEOUT; - get_next_switch_counter(); // skip counter - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &out_report, report_size); - switchProState = SwitchOutputSubtypes::IDENTIFY; // done - switchProFinished = true; -#ifdef GAMEPAD_HOST_DEBUG - printf("Switch Pro controller initialized\n."); -#endif - Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); - gamepad->auxState.playerID.enabled = true; - break; - } - - default: - switchProState = SwitchOutputSubtypes::IDENTIFY; // reset - switchProFinished = false; - break; - } -} - -void GamepadUSBHostListener::update_switch_pro() -{ -#if GAMEPAD_HOST_USE_FEATURES - if (!switchProFinished) return; - - Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); - - SwitchProHostReport out_report{ - .command = SwitchCommands::SPI_READ, - .counter = get_next_switch_counter(), - .rumble_l = {0x00, 0x01, 0x40, 0x40}, // default rumble states - .rumble_r = {0x00, 0x01, 0x40, 0x40}, - }; - uint8_t report_size = 10; // no subcommand - - // rumble - gamepad->auxState.haptics.leftActuator.enabled = 1; - gamepad->auxState.haptics.rightActuator.enabled = 1; - if (gamepad->auxState.haptics.leftActuator.active - && gamepad->auxState.haptics.leftActuator.intensity > 0) { - // personally I felt this a bit too strong on PS4 games, might need to be adjusted for personal preference - uint8_t amplitude_l = static_cast(((gamepad->auxState.haptics.leftActuator.intensity / 255.0f) * 0.8f + 0.5f) * (0xC0 - 0x40) + 0x40); -#ifdef GAMEPAD_HOST_DEBUG - printf("Switch Pro Left Rumble Intensity: %d -> %d\n", gamepad->auxState.haptics.leftActuator.intensity, amplitude_l); -#endif - - out_report.rumble_l[0] = amplitude_l; - out_report.rumble_l[1] = 0x88; - out_report.rumble_l[2] = amplitude_l / 2; - out_report.rumble_l[3] = 0x61; - } - if (gamepad->auxState.haptics.rightActuator.active - && gamepad->auxState.haptics.leftActuator.intensity > 0) { - uint8_t amplitude_r = static_cast(((gamepad->auxState.haptics.rightActuator.intensity / 255.0f) * 0.8f + 0.5f) * (0xC0 - 0x40) + 0x40); -#ifdef GAMEPAD_HOST_DEBUG - printf("Switch Pro Right Rumble Intensity: %d -> %d\n", gamepad->auxState.haptics.rightActuator.intensity, amplitude_r); -#endif - out_report.rumble_r[0] = amplitude_r; - out_report.rumble_r[1] = 0x88; - out_report.rumble_r[2] = amplitude_r / 2; - out_report.rumble_r[3] = 0x61; - } - - // Player light indicator - if (gamepad->auxState.playerID.active) { - Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); - uint8_t shifted = (1u << (gamepad->auxState.playerID.value+1u)) - 1u; - if (gamepad->auxState.playerID.value < 1 && gamepad->auxState.playerID.value > 4) { - shifted = 1; - } - if (shifted != lastSwitchLed) { - SwitchProHostReport led_out_report{ - .command = SwitchOutputSubtypes::IDENTIFY, - .counter = get_next_switch_counter(), - .rumble_l = {0x00, 0x01, 0x40, 0x40}, - .rumble_r = {0x00, 0x01, 0x40, 0x40}, - .subcommand = SwitchCommands::SET_PLAYER_LIGHTS, - .subcommand_args = {shifted, 0x00, 0x00}, - }; - uint8_t report_size = 12; // 10 + 2 for subcommand - - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &led_out_report, report_size); - } - lastSwitchLed = shifted; - } - - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &out_report, report_size); -#endif -} - -void GamepadUSBHostListener::process_switch_pro(uint8_t const *report, uint16_t len) -{ - if (len == 0) return; - if (!switchProFinished) { -#ifdef GAMEPAD_HOST_DEBUG - printf("received report id %x during initialization\n", report[0]); -#endif - setup_switch_pro(report, len); - return; - } - - SwitchProReport controller_report; - - static SwitchProReport prev_report = { 0 }; - - if (len < sizeof(SwitchProReport)) { -#ifdef GAMEPAD_HOST_DEBUG - printf("ignoring report with len %d (%x)...\n", len, report[0]); -#endif - return; - } - if (report[0] != 0x30) { -#ifdef GAMEPAD_HOST_DEBUG - printf("ignoring report with id %d (%d len)...\n", report[0], len); -#endif - return; - } - memcpy(&controller_report, report, sizeof(controller_report)); - - if (memcmp(&prev_report, &controller_report, sizeof(SwitchProReport)) == 0) - return; - - _controller_host_state.dpad = 0; - if (controller_report.inputs.dpadUp) _controller_host_state.dpad |= GAMEPAD_MASK_UP; - if (controller_report.inputs.dpadDown) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; - if (controller_report.inputs.dpadLeft) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; - if (controller_report.inputs.dpadRight) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; - - _controller_host_state.buttons = 0; - if (controller_report.inputs.buttonY) _controller_host_state.buttons |= GAMEPAD_MASK_B3; - if (controller_report.inputs.buttonX) _controller_host_state.buttons |= GAMEPAD_MASK_B4; - if (controller_report.inputs.buttonB) _controller_host_state.buttons |= GAMEPAD_MASK_B1; - if (controller_report.inputs.buttonA) _controller_host_state.buttons |= GAMEPAD_MASK_B2; - if (controller_report.inputs.buttonR) _controller_host_state.buttons |= GAMEPAD_MASK_R1; - if (controller_report.inputs.buttonZR) _controller_host_state.buttons |= GAMEPAD_MASK_R2; - if (controller_report.inputs.buttonMinus) _controller_host_state.buttons |= GAMEPAD_MASK_S1; - if (controller_report.inputs.buttonPlus) _controller_host_state.buttons |= GAMEPAD_MASK_S2; - if (controller_report.inputs.buttonThumbR) _controller_host_state.buttons |= GAMEPAD_MASK_R3; - if (controller_report.inputs.buttonThumbL) _controller_host_state.buttons |= GAMEPAD_MASK_L3; - if (controller_report.inputs.buttonHome) _controller_host_state.buttons |= GAMEPAD_MASK_A1; - if (controller_report.inputs.buttonCapture) _controller_host_state.buttons |= GAMEPAD_MASK_A2; - if (controller_report.inputs.buttonL) _controller_host_state.buttons |= GAMEPAD_MASK_L1; - if (controller_report.inputs.buttonZL) _controller_host_state.buttons |= GAMEPAD_MASK_L2; - - uint16_t lx12 = controller_report.inputs.leftStick.getX() & 0x0FFFu; - uint16_t ly12 = (0x1000u - (controller_report.inputs.leftStick.getY() & 0x0FFFu)) & 0x0FFFu; - uint16_t rx12 = controller_report.inputs.rightStick.getX() & 0x0FFFu; - uint16_t ry12 = (0x1000u - (controller_report.inputs.rightStick.getY() & 0x0FFFu)) & 0x0FFFu; - - _controller_host_state.lx = map(lx12, 0, 4095, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ly = map(ly12, 0, 4095, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); - _controller_host_state.rx = map(rx12, 0, 4095, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ry = map(ry12, 0, 4095, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); - _controller_host_analog = false; - - prev_report = controller_report; -} - -uint8_t GamepadUSBHostListener::get_next_switch_counter() -{ - if (switchReportCounter < 255) { - switchReportCounter++; - } else { - switchReportCounter = 0; - } - return switchReportCounter; -} - -void GamepadUSBHostListener::process_stadia(uint8_t const *report, uint16_t len) -{ - google_stadia_report_t controller_report; - - memcpy(&controller_report, report, sizeof(controller_report)); - - _controller_host_state.lx = map(controller_report.GD_GamePadPointerX ,1,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ly = map(controller_report.GD_GamePadPointerY,1 ,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.rx = map(controller_report.GD_GamePadPointerZ,1 ,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ry = map(controller_report.GD_GamePadPointerRz,1 ,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.lt = controller_report.SIM_GamePadBrake; - _controller_host_state.rt = controller_report.SIM_GamePadAccelerator; - _controller_host_analog = true; - - if (controller_report.BTN_GamePadButton18 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_A2; - if (controller_report.BTN_GamePadButton17 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_A3; - if (controller_report.BTN_GamePadButton11 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_S1; - if (controller_report.BTN_GamePadButton15 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R3; - if (controller_report.BTN_GamePadButton14 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_L3; - if (controller_report.BTN_GamePadButton13 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_A1; - if (controller_report.BTN_GamePadButton12 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_S2; - if (controller_report.BTN_GamePadButton8 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R1; - if (controller_report.BTN_GamePadButton7 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_L1; - if (controller_report.BTN_GamePadButton5 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B4; - if (controller_report.BTN_GamePadButton4 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B3; - if (controller_report.BTN_GamePadButton2 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B2; - if (controller_report.BTN_GamePadButton1 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B1; - if (controller_report.BTN_GamePadButton19 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R2; - if (controller_report.BTN_GamePadButton20 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_L2; - - if (controller_report.GD_GamePadHatSwitch == 0) _controller_host_state.dpad |= GAMEPAD_MASK_UP; - if (controller_report.GD_GamePadHatSwitch == 1) _controller_host_state.dpad |= GAMEPAD_MASK_UP | GAMEPAD_MASK_RIGHT; - if (controller_report.GD_GamePadHatSwitch == 2) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; - if (controller_report.GD_GamePadHatSwitch == 3) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT | GAMEPAD_MASK_DOWN; - if (controller_report.GD_GamePadHatSwitch == 4) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; - if (controller_report.GD_GamePadHatSwitch == 5) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN | GAMEPAD_MASK_LEFT; - if (controller_report.GD_GamePadHatSwitch == 6) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; - if (controller_report.GD_GamePadHatSwitch == 7) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT | GAMEPAD_MASK_UP; -} - -void GamepadUSBHostListener::setup_df_wheel() { - // send commands to see if can be reset to Driving Force GT mode for more compatibility - uint8_t command[8] = {0xF8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00}; - uint16_t commandSize = sizeof(command); - - if (tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, command, commandSize)) { - isDFInit = true; - } -} - -void GamepadUSBHostListener::process_dfgt(uint8_t const* report, uint16_t len) { - PS3ReportAlt ps3Report; - memcpy(&ps3Report, report, len); -#if GAMEPAD_HOST_DEBUG - //printf("\033[2;0H"); - //for (uint8_t i = 0; i < len; i++) { - // printf("%02x ", report[i]); - // if (((i+1) % 16) == 0) printf("\n"); - //} - //printf("\033[2;0H Ste:%5d Gas:%3d Brk:%3d", ps3Report.wheel.steeringWheel, ps3Report.wheel.gasPedal, ps3Report.wheel.brakePedal); - //printf("\n-----\n"); - //printf("\033[2;0HDPad: %1d", ps3Report.wheel.dpadDirection); - //printf("\033[3;0HWheel: %5d", ps3Report.wheel.steeringWheel); - //printf("\033[4;0HGas: %3d", ps3Report.wheel.gasPedal); - //printf("\033[5;0HBrake: %3d", ps3Report.wheel.brakePedal); -#endif -} - -void GamepadUSBHostListener::process_ultrastik360(uint8_t const* report, uint16_t len) { - - ultrastik360_t controller_report; - - memcpy(&controller_report, report, sizeof(controller_report)); - - _controller_host_state.lx = map(controller_report.GD_GamePadPointerX, 0, 255, GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ly = map(controller_report.GD_GamePadPointerY, 0, 255, GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_analog = true; - - if (controller_report.BTN_GamePadButton1 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B1; - if (controller_report.BTN_GamePadButton2 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B2; - if (controller_report.BTN_GamePadButton3 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B3; - if (controller_report.BTN_GamePadButton4 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B4; - if (controller_report.BTN_GamePadButton5 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_L1; - if (controller_report.BTN_GamePadButton6 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_L2; - if (controller_report.BTN_GamePadButton7 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R1; - if (controller_report.BTN_GamePadButton8 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R2; -} - -void GamepadUSBHostListener::xbox360_set_led(uint8_t dev_addr, uint8_t instance, uint8_t quadrant) { - uint8_t out[32] = { 0 }; - - memcpy(out, XBOX360_WIRED_LED, sizeof(XBOX360_WIRED_LED)); - out[2] = (quadrant == 0) ? 0 : (quadrant + 5); - bool ret = tuh_xinput_send_report(dev_addr, instance, out, sizeof(XBOX360_WIRED_LED)); - if (ret) { - tuh_xinput_wait_for_tx(dev_addr, instance); - } -} - -void GamepadUSBHostListener::xinput_set_rumble(uint8_t dev_addr, uint8_t instance, uint8_t left, uint8_t right) { - uint8_t out[32] = { 0 }; - uint16_t len = 0; - switch (_controller_type) { - case xinput_type_t::XBOX360: { - memcpy(out, XBOX360_WIRED_RUMBLE, sizeof(XBOX360_WIRED_RUMBLE)); - out[3] = left; - out[4] = right; - len = sizeof(XBOX360_WIRED_RUMBLE); - break; - } - case xinput_type_t::XBOXONE: { - memcpy(out, XBOXONE_RUMBLE, sizeof(XBOXONE_RUMBLE)); - out[8] = left >> 1; // 7-bit (0-127) - out[9] = right >> 1; // 7-bit (0-127) - len = sizeof(XBOXONE_RUMBLE); - break; - } - default: - return; - } - tuh_xinput_wait_for_tx(dev_addr, instance); - bool ret = tuh_xinput_send_report(dev_addr, instance, out, len); - if (ret) { - tuh_xinput_wait_for_tx(dev_addr, instance); - } -} - -void GamepadUSBHostListener::setup_xinput(uint8_t dev_addr, uint8_t instance) { - Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); - - switch (_controller_type) { - case xinput_type_t::XBOX360: { - uint32_t quadrants = gamepad->auxState.playerID.value; - if (quadrants == 0) - quadrants = 1; - for (uint32_t i = 0; i < quadrants; i++) { - xbox360_set_led(dev_addr, instance, i); - } - - xinput_set_rumble(dev_addr, instance, 0, 0); - break; - } - case xinput_type_t::XBOXONE: { - // implement xbox one init here... currently not supported - break; - } - default: // unsupported - break; - } - tuh_xinput_receive_report(dev_addr, instance); -} - -void GamepadUSBHostListener::update_xinput(uint8_t dev_addr, uint8_t instance) { - Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); - - static uint8_t last_left_rumble = 0; - static uint8_t last_right_rumble = 0; - - // rumble - gamepad->auxState.haptics.leftActuator.enabled = 1; - gamepad->auxState.haptics.rightActuator.enabled = 1; - uint8_t leftRumble = 0; - uint8_t rightRumble = 0; - if (gamepad->auxState.haptics.leftActuator.active) { - leftRumble = gamepad->auxState.haptics.leftActuator.intensity; - } - if (gamepad->auxState.haptics.rightActuator.active) { - rightRumble = gamepad->auxState.haptics.rightActuator.intensity; - } - - if (leftRumble == last_left_rumble && rightRumble == last_right_rumble) { - return; // no change - } - last_left_rumble = leftRumble; - last_right_rumble = rightRumble; - - xinput_set_rumble(dev_addr, instance, leftRumble, rightRumble); -} - -void GamepadUSBHostListener::process_xbox360(uint8_t const* report, uint16_t len) { - XInputReport controller_report; - - static XInputReport prev_report = { 0 }; - - if (len < sizeof(XInputReport)) { -#if GAMEPAD_HOST_DEBUG - printf("Xbox 360 report too small: %d bytes\n", len); -#endif - return; - } - - memcpy(&controller_report, report, sizeof(controller_report)); - - if (memcmp(&prev_report, &controller_report, sizeof(XInputReport)) == 0) - return; - - _controller_host_state.dpad = 0; - if (controller_report.buttons1 & XBOX_MASK_UP) _controller_host_state.dpad |= GAMEPAD_MASK_UP; - if (controller_report.buttons1 & XBOX_MASK_DOWN) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; - if (controller_report.buttons1 & XBOX_MASK_LEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; - if (controller_report.buttons1 & XBOX_MASK_RIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; - - _controller_host_state.buttons = 0; - if (controller_report.buttons1 & XBOX_MASK_START) _controller_host_state.buttons |= GAMEPAD_MASK_S2; - if (controller_report.buttons1 & XBOX_MASK_BACK) _controller_host_state.buttons |= GAMEPAD_MASK_S1; - if (controller_report.buttons1 & XBOX_MASK_LS) _controller_host_state.buttons |= GAMEPAD_MASK_L3; - if (controller_report.buttons1 & XBOX_MASK_RS) _controller_host_state.buttons |= GAMEPAD_MASK_R3; - if (controller_report.buttons2 & XBOX_MASK_LB) _controller_host_state.buttons |= GAMEPAD_MASK_L1; - if (controller_report.buttons2 & XBOX_MASK_RB) _controller_host_state.buttons |= GAMEPAD_MASK_R1; - if (controller_report.buttons2 & XBOX_MASK_HOME) _controller_host_state.buttons |= GAMEPAD_MASK_A1; - if (controller_report.buttons2 & XBOX_MASK_A) _controller_host_state.buttons |= GAMEPAD_MASK_B1; - if (controller_report.buttons2 & XBOX_MASK_B) _controller_host_state.buttons |= GAMEPAD_MASK_B2; - if (controller_report.buttons2 & XBOX_MASK_X) _controller_host_state.buttons |= GAMEPAD_MASK_B3; - if (controller_report.buttons2 & XBOX_MASK_Y) _controller_host_state.buttons |= GAMEPAD_MASK_B4; - - _controller_host_state.lx = static_cast(controller_report.lx - INT16_MIN); - _controller_host_state.ly = ~static_cast(controller_report.ly - INT16_MIN); - _controller_host_state.rx = static_cast(controller_report.rx - INT16_MIN); - _controller_host_state.ry = ~static_cast(controller_report.ry - INT16_MIN); - - _controller_host_state.lt = controller_report.lt; - _controller_host_state.rt = controller_report.rt; - _controller_host_analog = true; - - prev_report = controller_report; } diff --git a/src/hosts/DrivingForceHost.cpp b/src/hosts/DrivingForceHost.cpp new file mode 100644 index 0000000000..4e993c62cc --- /dev/null +++ b/src/hosts/DrivingForceHost.cpp @@ -0,0 +1,48 @@ +#include "hosts/DrivingForceHost.h" + + +/* + // Logitech G29 Driving Force + if ( vendor_id == ) { + switch(product_id) { + case 0xC260: + return true; + } + } + + // Thrustmaster T.Flight Racing Wheel + if ( vendor_id == 0x44F) { + switch(product_id) { + case 0xB67B: + return true; + } + } +*/ + +void DrivingForceHost::intialize() { + // send commands to see if can be reset to Driving Force GT mode for more compatibility + uint8_t command[8] = {0xF8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00}; + uint16_t commandSize = sizeof(command); + + if (tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, command, commandSize)) { + isDFInit = true; + } +} + +void DrivingForceHost::process(uint8_t const* report, uint16_t len) { + PS3ReportAlt ps3Report; + memcpy(&ps3Report, report, len); +#if GAMEPAD_HOST_DEBUG + //printf("\033[2;0H"); + //for (uint8_t i = 0; i < len; i++) { + // printf("%02x ", report[i]); + // if (((i+1) % 16) == 0) printf("\n"); + //} + //printf("\033[2;0H Ste:%5d Gas:%3d Brk:%3d", ps3Report.wheel.steeringWheel, ps3Report.wheel.gasPedal, ps3Report.wheel.brakePedal); + //printf("\n-----\n"); + //printf("\033[2;0HDPad: %1d", ps3Report.wheel.dpadDirection); + //printf("\033[3;0HWheel: %5d", ps3Report.wheel.steeringWheel); + //printf("\033[4;0HGas: %3d", ps3Report.wheel.gasPedal); + //printf("\033[5;0HBrake: %3d", ps3Report.wheel.brakePedal); +#endif +} \ No newline at end of file diff --git a/src/hosts/DualsensePS5Host.cpp b/src/hosts/DualsensePS5Host.cpp new file mode 100644 index 0000000000..b4e32e15ce --- /dev/null +++ b/src/hosts/DualsensePS5Host.cpp @@ -0,0 +1,69 @@ +#include "hosts/DualsensePS5Host.h" + +void DualsensePS5Host::initialize() { + requiresInit = false; +} + +bool DualsensePS5Host::match(uint8_t vendor_id, uint8_t product_id) { + // Playstation 5 Dualsense + switch(product_id) { + case 0x0CE6: // DualSense + return true; + default: + break; + } + + return false; +} + + +void DualsensePS5Host::process(uint8_t const* report, uint16_t len) { + DSReport controller_report; + + // previous report used to compare for changes + static DSReport prev_ds_report = { 0 }; + + uint8_t const report_id = report[0]; + + if (report_id == 1) { + memcpy(&controller_report, report, sizeof(controller_report)); + + if ( prev_ds_report.reportCounter != controller_report.reportCounter ) { + _controller_host_state.lx = map(controller_report.leftStickX, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ly = map(controller_report.leftStickY, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.rx = map(controller_report.rightStickX,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ry = map(controller_report.rightStickY,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.lt = controller_report.leftTrigger; + _controller_host_state.rt = controller_report.rightTrigger; + _controller_host_analog = true; + + _controller_host_state.buttons = 0; + if (controller_report.buttonTouchpad) _controller_host_state.buttons |= GAMEPAD_MASK_A2; + if (controller_report.buttonSelect) _controller_host_state.buttons |= GAMEPAD_MASK_S1; + if (controller_report.buttonR3) _controller_host_state.buttons |= GAMEPAD_MASK_R3; + if (controller_report.buttonL3) _controller_host_state.buttons |= GAMEPAD_MASK_L3; + if (controller_report.buttonHome) _controller_host_state.buttons |= GAMEPAD_MASK_A1; + if (controller_report.buttonStart) _controller_host_state.buttons |= GAMEPAD_MASK_S2; + if (controller_report.buttonR1) _controller_host_state.buttons |= GAMEPAD_MASK_R1; + if (controller_report.buttonL1) _controller_host_state.buttons |= GAMEPAD_MASK_L1; + if (controller_report.buttonNorth) _controller_host_state.buttons |= GAMEPAD_MASK_B4; + if (controller_report.buttonEast) _controller_host_state.buttons |= GAMEPAD_MASK_B2; + if (controller_report.buttonSouth) _controller_host_state.buttons |= GAMEPAD_MASK_B1; + if (controller_report.buttonWest) _controller_host_state.buttons |= GAMEPAD_MASK_B3; + if (controller_report.buttonR2) _controller_host_state.buttons |= GAMEPAD_MASK_R2; + if (controller_report.buttonL2) _controller_host_state.buttons |= GAMEPAD_MASK_L2; + + _controller_host_state.dpad = 0; + if (controller_report.dpad == PS4_HAT_UP) _controller_host_state.dpad |= GAMEPAD_MASK_UP; + if (controller_report.dpad == PS4_HAT_UPRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_UP | GAMEPAD_MASK_RIGHT; + if (controller_report.dpad == PS4_HAT_RIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; + if (controller_report.dpad == PS4_HAT_DOWNRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT | GAMEPAD_MASK_DOWN; + if (controller_report.dpad == PS4_HAT_DOWN) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; + if (controller_report.dpad == PS4_HAT_DOWNLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN | GAMEPAD_MASK_LEFT; + if (controller_report.dpad == PS4_HAT_LEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; + if (controller_report.dpad == PS4_HAT_UPLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT | GAMEPAD_MASK_UP; + } + } + + prev_ds_report = controller_report; +} diff --git a/src/hosts/DualshockPS4Host.cpp b/src/hosts/DualshockPS4Host.cpp new file mode 100644 index 0000000000..8131498d79 --- /dev/null +++ b/src/hosts/DualshockPS4Host.cpp @@ -0,0 +1,206 @@ +#include "hosts/DualshockPS4Host.h" + +#include "storagemanager.h" + +// Static match function +bool DualshockPS4Host::match(uint16_t vendor_id, uint16_t product_id) { + // Hori (PS4) + if ( vendor_id == 0x0F0D ) { + switch(product_id) { + case 0x00EE: + return true; + } + } + + // Razer (PS4) + if ( vendor_id == 0x1532 ) { + switch(product_id) { + case 0x0401: // Razer Panthera + case 0x1008: // Razer Panthera Evo (PS4 mode) + return true; + } + } + + // Sony (Dualshock4 variations) + if ( vendor_id == 0x054C) { + switch(product_id) { + case 0x05C4: // [CUH-ZCT1x] + case 0x09CC: // [CUH-ZCT2x] + return true; + } + } + + return false; +} + +// We can just force it to assume its a dualshock, why do we care about definition? +void DualshockPS4Host::initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { + // Setup our vars + _dev_addr = dev_addr; + _instance = instance; + _vendor_id = vendor_id; + _product_id = product_id; + + _controller_host_state.buttons = 0; + _controller_host_state.dpad = 0; + _controller_host_state.lx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ly = GAMEPAD_JOYSTICK_MID; + _controller_host_state.rx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ry = GAMEPAD_JOYSTICK_MID; + + memset(&prevReport, 0, sizeof(PS4Report)); + + validDS4HID = false; // check the report def + + // Find the report for PS4 definition if this controller has one + tuh_hid_report_info_t report_info[4]; + uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 4, desc_report, desc_len); + for(uint8_t i = 0; i < report_count; i++) { +#if GAMEPAD_HOST_DEBUG + //printf("Report: %02x, Usage: %04x, Usage Page: %04x\n", report_info[i].report_id, report_info[i].usage_page, report_info[i].usage); +#endif + if (report_info[i].report_id == PS4AuthReport::PS4_DEFINITION) { + // controller is some other type that's not a DS4, so parse the config + memset(report_buffer, 0, PS4_ENDPOINT_SIZE); + report_buffer[0] = PS4AuthReport::PS4_DEFINITION; + tuh_hid_get_report(dev_addr, instance, PS4AuthReport::PS4_DEFINITION, HID_REPORT_TYPE_FEATURE, report_buffer, 48); + memcpy(&ds4Config, report_buffer+1, sizeof(PS4ControllerConfig)); + if ((ds4Config.hidUsage == 0x2721) || (ds4Config.hidUsage == 0x2127)) { + validDS4HID = true; + #if GAMEPAD_HOST_DEBUG + //printf("PS4 controller details\n"); + //printf("----------------------\n"); + //printf("enableController: %d\n", ds4Config.features.enableController); + //printf("enableMotion: %d\n", ds4Config.features.enableMotion); + //printf("enableLED: %d\n", ds4Config.features.enableLED); + //printf("enableRumble: %d\n", ds4Config.features.enableRumble); + //printf("enableAnalog: %d\n", ds4Config.features.enableAnalog); + //printf("enableUnknown0: %d\n", ds4Config.features.enableUnknown0); + //printf("enableTouchpad: %d\n", ds4Config.features.enableTouchpad); + //printf("enableUnknown1: %d\n", ds4Config.features.enableUnknown1); + #endif + } + + break; + } + } +} + +void DualshockPS4Host::update() { + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + PS4FeatureOutputReport controller_output; // this might have to be static + memset(&controller_output, 0, sizeof(controller_output)); + controller_output.reportID = PS4AuthReport::PS4_SET_FEATURE_STATE; + if (ds4Config.features.enableLED && gamepad->auxState.sensors.statusLight.enabled) { + controller_output.enableUpdateLED = gamepad->auxState.sensors.statusLight.enabled; + controller_output.ledRed = gamepad->auxState.sensors.statusLight.color.red; + controller_output.ledGreen = gamepad->auxState.sensors.statusLight.color.green; + controller_output.ledBlue = gamepad->auxState.sensors.statusLight.color.blue; + controller_output.ledBlinkOn = gamepad->auxState.playerID.ledBlinkOn; + controller_output.ledBlinkOff = gamepad->auxState.playerID.ledBlinkOff; + } + + if (ds4Config.features.enableRumble) { + gamepad->auxState.haptics.leftActuator.enabled = 1; + gamepad->auxState.haptics.rightActuator.enabled = 1; + controller_output.enableUpdateRumble = 1; + controller_output.rumbleLeft = gamepad->auxState.haptics.leftActuator.intensity; + controller_output.rumbleRight = gamepad->auxState.haptics.rightActuator.intensity; + } + + void * report = &controller_output; + uint16_t report_size = sizeof(controller_output)-1; + + tuh_hid_send_report(_dev_addr, _instance, PS4AuthReport::PS4_SET_FEATURE_STATE, (uint8_t*)report+1, report_size); +} + + +void DualshockPS4Host::process(uint8_t const* report, uint16_t len) { + PS4Report controller_report; + + // If its an input report + if (report[0] == 1) { + memcpy(&controller_report, report, sizeof(controller_report)); + + if ( diff_report(&prevReport, &controller_report) ) { + _controller_host_state.lx = map(controller_report.leftStickX, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ly = map(controller_report.leftStickY, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.rx = map(controller_report.rightStickX,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ry = map(controller_report.rightStickY,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.lt = controller_report.leftTrigger; + _controller_host_state.rt = controller_report.rightTrigger; + + _controller_host_state.buttons = 0; + if (controller_report.buttonTouchpad) _controller_host_state.buttons |= GAMEPAD_MASK_A2; + if (controller_report.buttonSelect) _controller_host_state.buttons |= GAMEPAD_MASK_S1; + if (controller_report.buttonR3) _controller_host_state.buttons |= GAMEPAD_MASK_R3; + if (controller_report.buttonL3) _controller_host_state.buttons |= GAMEPAD_MASK_L3; + if (controller_report.buttonHome) _controller_host_state.buttons |= GAMEPAD_MASK_A1; + if (controller_report.buttonStart) _controller_host_state.buttons |= GAMEPAD_MASK_S2; + if (controller_report.buttonR1) _controller_host_state.buttons |= GAMEPAD_MASK_R1; + if (controller_report.buttonL1) _controller_host_state.buttons |= GAMEPAD_MASK_L1; + if (controller_report.buttonNorth) _controller_host_state.buttons |= GAMEPAD_MASK_B4; + if (controller_report.buttonEast) _controller_host_state.buttons |= GAMEPAD_MASK_B2; + if (controller_report.buttonSouth) _controller_host_state.buttons |= GAMEPAD_MASK_B1; + if (controller_report.buttonWest) _controller_host_state.buttons |= GAMEPAD_MASK_B3; + if (controller_report.buttonR2) _controller_host_state.buttons |= GAMEPAD_MASK_R2; + if (controller_report.buttonL2) _controller_host_state.buttons |= GAMEPAD_MASK_L2; + + _controller_host_state.dpad = 0; + if (controller_report.dpad == PS4_HAT_UP) _controller_host_state.dpad |= GAMEPAD_MASK_UP; + if (controller_report.dpad == PS4_HAT_UPRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_UP | GAMEPAD_MASK_RIGHT; + if (controller_report.dpad == PS4_HAT_RIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; + if (controller_report.dpad == PS4_HAT_DOWNRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT | GAMEPAD_MASK_DOWN; + if (controller_report.dpad == PS4_HAT_DOWN) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; + if (controller_report.dpad == PS4_HAT_DOWNLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN | GAMEPAD_MASK_LEFT; + if (controller_report.dpad == PS4_HAT_LEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; + if (controller_report.dpad == PS4_HAT_UPLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT | GAMEPAD_MASK_UP; + } + + // Save report to previous + memcpy(&prevReport, &controller_report, sizeof(PS4Report)); + } +} + +void DualshockPS4Host::gamepad(Gamepad * gamepad) { + // Override the Gamepad + gamepad->hasAnalogTriggers = true; + gamepad->hasLeftAnalogStick = true; + gamepad->hasRightAnalogStick = true; + gamepad->state.dpad |= _controller_host_state.dpad; + gamepad->state.buttons |= _controller_host_state.buttons; + gamepad->state.lx = _controller_host_state.lx; + gamepad->state.ly = _controller_host_state.ly; + gamepad->state.rx = _controller_host_state.rx; + gamepad->state.ry = _controller_host_state.ry; + gamepad->state.rt = _controller_host_state.rt; + gamepad->state.lt = _controller_host_state.lt; +} + +void DualshockPS4Host::shutdown() { +} + + + +uint32_t DualshockPS4Host::map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +//check if different than 2 +bool DualshockPS4Host::diff_than_2(uint8_t x, uint8_t y) { + return (x - y > 2) || (y - x > 2); +} + +// check if 2 reports are different enough +bool DualshockPS4Host::diff_report(PS4Report const* rpt1, PS4Report const* rpt2) { + bool result; + + // x, y, z, rz must different than 2 to be counted + result = diff_than_2(rpt1->leftStickX, rpt2->leftStickX) || diff_than_2(rpt1->leftStickY, rpt2->leftStickY) || + diff_than_2(rpt1->rightStickX, rpt2->rightStickX) || diff_than_2(rpt1->rightStickY, rpt2->rightStickY); + + // check the rest with mem compare + result |= memcmp(&rpt1->rightStickY + 1, &rpt2->rightStickY + 1, sizeof(PS4Report)-6); + + return result; +} diff --git a/src/hosts/GoogleStadioHost.cpp b/src/hosts/GoogleStadioHost.cpp new file mode 100644 index 0000000000..967103fd10 --- /dev/null +++ b/src/hosts/GoogleStadioHost.cpp @@ -0,0 +1,40 @@ +#include "hosts/GoogleStadiaHost.h" + +void GoogleStadioHost::process(uint8_t const *report, uint16_t len) { + google_stadia_report_t controller_report; + + memcpy(&controller_report, report, sizeof(controller_report)); + + _controller_host_state.lx = map(controller_report.GD_GamePadPointerX ,1,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ly = map(controller_report.GD_GamePadPointerY,1 ,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.rx = map(controller_report.GD_GamePadPointerZ,1 ,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ry = map(controller_report.GD_GamePadPointerRz,1 ,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.lt = controller_report.SIM_GamePadBrake; + _controller_host_state.rt = controller_report.SIM_GamePadAccelerator; + _controller_host_analog = true; + + if (controller_report.BTN_GamePadButton18 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_A2; + if (controller_report.BTN_GamePadButton17 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_A3; + if (controller_report.BTN_GamePadButton11 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_S1; + if (controller_report.BTN_GamePadButton15 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R3; + if (controller_report.BTN_GamePadButton14 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_L3; + if (controller_report.BTN_GamePadButton13 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_A1; + if (controller_report.BTN_GamePadButton12 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_S2; + if (controller_report.BTN_GamePadButton8 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R1; + if (controller_report.BTN_GamePadButton7 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_L1; + if (controller_report.BTN_GamePadButton5 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B4; + if (controller_report.BTN_GamePadButton4 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B3; + if (controller_report.BTN_GamePadButton2 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B2; + if (controller_report.BTN_GamePadButton1 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B1; + if (controller_report.BTN_GamePadButton19 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R2; + if (controller_report.BTN_GamePadButton20 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_L2; + + if (controller_report.GD_GamePadHatSwitch == 0) _controller_host_state.dpad |= GAMEPAD_MASK_UP; + if (controller_report.GD_GamePadHatSwitch == 1) _controller_host_state.dpad |= GAMEPAD_MASK_UP | GAMEPAD_MASK_RIGHT; + if (controller_report.GD_GamePadHatSwitch == 2) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; + if (controller_report.GD_GamePadHatSwitch == 3) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT | GAMEPAD_MASK_DOWN; + if (controller_report.GD_GamePadHatSwitch == 4) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; + if (controller_report.GD_GamePadHatSwitch == 5) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN | GAMEPAD_MASK_LEFT; + if (controller_report.GD_GamePadHatSwitch == 6) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; + if (controller_report.GD_GamePadHatSwitch == 7) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT | GAMEPAD_MASK_UP; +} diff --git a/src/hosts/SwitchProHost.cpp b/src/hosts/SwitchProHost.cpp new file mode 100644 index 0000000000..d8f7f7ef85 --- /dev/null +++ b/src/hosts/SwitchProHost.cpp @@ -0,0 +1,214 @@ +#include "SwitchProHost.h" + + +#undef GAMEPAD_HOST_DEBUG + +// init phase +// https://github.com/wiredopposite/OGX-Mini/blob/ccccf6651b54bcca3c94b5d4e73e8f2796fc8c05/Firmware/RP2040/src/USBHost/HostDriver/SwitchPro/SwitchPro.cpp#L23 +void SwitchProHost::initialize(uint8_t vendor_id, uint8_t product_id, uint8_t const* desc_report, uint16_t desc_len) { + SwitchProHostReport out_report{ + .rumble_l = {0x00, 0x01, 0x40, 0x40}, // default rumble states + .rumble_r = {0x00, 0x01, 0x40, 0x40}, + }; + uint8_t report_size = 10; // no subcommand + +#ifdef GAMEPAD_HOST_DEBUG + printf("Switch Pro init state: %d\n.", switchProState); +#endif + + switch (switchProState) + { + case SwitchOutputSubtypes::IDENTIFY: { + if (len < 10 + || report[0] != SwitchReportID::REPORT_USB_INPUT_81 + || report[1] != SwitchOutputSubtypes::IDENTIFY) { + tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &SWITCH_INIT_REPORT, sizeof(SWITCH_INIT_REPORT)); + break; + } + + out_report.command = SwitchReportID::REPORT_CONFIGURATION; + out_report.counter = SwitchOutputSubtypes::HANDSHAKE; + get_next_switch_counter(); // skip counter + tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &out_report, report_size); + // might turn up the home LED as well + switchProState = SwitchOutputSubtypes::DISABLE_USB_TIMEOUT; + break; + } + case SwitchOutputSubtypes::DISABLE_USB_TIMEOUT: { + if (len < 2 || + (report[0] != SwitchReportID::REPORT_USB_INPUT_81 + && report[0] != SwitchReportID::REPORT_OUTPUT_30)) { + // reset + tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &SWITCH_INIT_REPORT, sizeof(SWITCH_INIT_REPORT)); + switchProState = SwitchOutputSubtypes::IDENTIFY; + break; + } + out_report.command = SwitchReportID::REPORT_CONFIGURATION; + out_report.counter = SwitchOutputSubtypes::DISABLE_USB_TIMEOUT; + get_next_switch_counter(); // skip counter + tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &out_report, report_size); + switchProState = SwitchOutputSubtypes::IDENTIFY; // done + switchProFinished = true; +#ifdef GAMEPAD_HOST_DEBUG + printf("Switch Pro controller initialized\n."); +#endif + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + gamepad->auxState.playerID.enabled = true; + break; + } + + default: + switchProState = SwitchOutputSubtypes::IDENTIFY; // reset + switchProFinished = false; + break; + } +} + + +void SwitchProHost::update() +{ + if (!switchProFinished) return; + + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + + SwitchProHostReport out_report{ + .command = SwitchCommands::SPI_READ, + .counter = get_next_switch_counter(), + .rumble_l = {0x00, 0x01, 0x40, 0x40}, // default rumble states + .rumble_r = {0x00, 0x01, 0x40, 0x40}, + }; + uint8_t report_size = 10; // no subcommand + + // rumble + gamepad->auxState.haptics.leftActuator.enabled = 1; + gamepad->auxState.haptics.rightActuator.enabled = 1; + if (gamepad->auxState.haptics.leftActuator.active + && gamepad->auxState.haptics.leftActuator.intensity > 0) { + // personally I felt this a bit too strong on PS4 games, might need to be adjusted for personal preference + uint8_t amplitude_l = static_cast(((gamepad->auxState.haptics.leftActuator.intensity / 255.0f) * 0.8f + 0.5f) * (0xC0 - 0x40) + 0x40); +#ifdef GAMEPAD_HOST_DEBUG + printf("Switch Pro Left Rumble Intensity: %d -> %d\n", gamepad->auxState.haptics.leftActuator.intensity, amplitude_l); +#endif + + out_report.rumble_l[0] = amplitude_l; + out_report.rumble_l[1] = 0x88; + out_report.rumble_l[2] = amplitude_l / 2; + out_report.rumble_l[3] = 0x61; + } + if (gamepad->auxState.haptics.rightActuator.active + && gamepad->auxState.haptics.leftActuator.intensity > 0) { + uint8_t amplitude_r = static_cast(((gamepad->auxState.haptics.rightActuator.intensity / 255.0f) * 0.8f + 0.5f) * (0xC0 - 0x40) + 0x40); +#ifdef GAMEPAD_HOST_DEBUG + printf("Switch Pro Right Rumble Intensity: %d -> %d\n", gamepad->auxState.haptics.rightActuator.intensity, amplitude_r); +#endif + out_report.rumble_r[0] = amplitude_r; + out_report.rumble_r[1] = 0x88; + out_report.rumble_r[2] = amplitude_r / 2; + out_report.rumble_r[3] = 0x61; + } + + // Player light indicator + if (gamepad->auxState.playerID.active) { + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + uint8_t shifted = (1u << (gamepad->auxState.playerID.value+1u)) - 1u; + if (gamepad->auxState.playerID.value < 1 && gamepad->auxState.playerID.value > 4) { + shifted = 1; + } + if (shifted != lastSwitchLed) { + SwitchProHostReport led_out_report{ + .command = SwitchOutputSubtypes::IDENTIFY, + .counter = get_next_switch_counter(), + .rumble_l = {0x00, 0x01, 0x40, 0x40}, + .rumble_r = {0x00, 0x01, 0x40, 0x40}, + .subcommand = SwitchCommands::SET_PLAYER_LIGHTS, + .subcommand_args = {shifted, 0x00, 0x00}, + }; + uint8_t report_size = 12; // 10 + 2 for subcommand + + tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &led_out_report, report_size); + } + lastSwitchLed = shifted; + } + + tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &out_report, report_size); +} + +uint8_t SwitchProHost::get_next_switch_counter() +{ + if (switchReportCounter < 255) { + switchReportCounter++; + } else { + switchReportCounter = 0; + } + return switchReportCounter; +} + + +void SwitchProHost::process(uint8_t const* report, uint16_t len) { +{ + if (len == 0) return; + if (!switchProFinished) { +#ifdef GAMEPAD_HOST_DEBUG + printf("received report id %x during initialization\n", report[0]); +#endif + setup_switch_pro(report, len); + return; + } + + SwitchProReport controller_report; + + static SwitchProReport prev_report = { 0 }; + + if (len < sizeof(SwitchProReport)) { +#ifdef GAMEPAD_HOST_DEBUG + printf("ignoring report with len %d (%x)...\n", len, report[0]); +#endif + return; + } + if (report[0] != 0x30) { +#ifdef GAMEPAD_HOST_DEBUG + printf("ignoring report with id %d (%d len)...\n", report[0], len); +#endif + return; + } + memcpy(&controller_report, report, sizeof(controller_report)); + + if (memcmp(&prev_report, &controller_report, sizeof(SwitchProReport)) == 0) + return; + + _controller_host_state.dpad = 0; + if (controller_report.inputs.dpadUp) _controller_host_state.dpad |= GAMEPAD_MASK_UP; + if (controller_report.inputs.dpadDown) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; + if (controller_report.inputs.dpadLeft) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; + if (controller_report.inputs.dpadRight) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; + + _controller_host_state.buttons = 0; + if (controller_report.inputs.buttonY) _controller_host_state.buttons |= GAMEPAD_MASK_B3; + if (controller_report.inputs.buttonX) _controller_host_state.buttons |= GAMEPAD_MASK_B4; + if (controller_report.inputs.buttonB) _controller_host_state.buttons |= GAMEPAD_MASK_B1; + if (controller_report.inputs.buttonA) _controller_host_state.buttons |= GAMEPAD_MASK_B2; + if (controller_report.inputs.buttonR) _controller_host_state.buttons |= GAMEPAD_MASK_R1; + if (controller_report.inputs.buttonZR) _controller_host_state.buttons |= GAMEPAD_MASK_R2; + if (controller_report.inputs.buttonMinus) _controller_host_state.buttons |= GAMEPAD_MASK_S1; + if (controller_report.inputs.buttonPlus) _controller_host_state.buttons |= GAMEPAD_MASK_S2; + if (controller_report.inputs.buttonThumbR) _controller_host_state.buttons |= GAMEPAD_MASK_R3; + if (controller_report.inputs.buttonThumbL) _controller_host_state.buttons |= GAMEPAD_MASK_L3; + if (controller_report.inputs.buttonHome) _controller_host_state.buttons |= GAMEPAD_MASK_A1; + if (controller_report.inputs.buttonCapture) _controller_host_state.buttons |= GAMEPAD_MASK_A2; + if (controller_report.inputs.buttonL) _controller_host_state.buttons |= GAMEPAD_MASK_L1; + if (controller_report.inputs.buttonZL) _controller_host_state.buttons |= GAMEPAD_MASK_L2; + + uint16_t lx12 = controller_report.inputs.leftStick.getX() & 0x0FFFu; + uint16_t ly12 = (0x1000u - (controller_report.inputs.leftStick.getY() & 0x0FFFu)) & 0x0FFFu; + uint16_t rx12 = controller_report.inputs.rightStick.getX() & 0x0FFFu; + uint16_t ry12 = (0x1000u - (controller_report.inputs.rightStick.getY() & 0x0FFFu)) & 0x0FFFu; + + _controller_host_state.lx = map(lx12, 0, 4095, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ly = map(ly12, 0, 4095, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); + _controller_host_state.rx = map(rx12, 0, 4095, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ry = map(ry12, 0, 4095, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); + _controller_host_analog = false; + + prev_report = controller_report; +} + diff --git a/src/hosts/Ultrastik360Host.cpp b/src/hosts/Ultrastik360Host.cpp new file mode 100644 index 0000000000..ec438d5d67 --- /dev/null +++ b/src/hosts/Ultrastik360Host.cpp @@ -0,0 +1,21 @@ +#include "hosts/Ultrastik360Host.h" + +void Ultrastik360Host::process(uint8_t const* report, uint16_t len) { + + ultrastik360_t controller_report; + + memcpy(&controller_report, report, sizeof(controller_report)); + + _controller_host_state.lx = map(controller_report.GD_GamePadPointerX, 0, 255, GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ly = map(controller_report.GD_GamePadPointerY, 0, 255, GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_analog = true; + + if (controller_report.BTN_GamePadButton1 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B1; + if (controller_report.BTN_GamePadButton2 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B2; + if (controller_report.BTN_GamePadButton3 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B3; + if (controller_report.BTN_GamePadButton4 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B4; + if (controller_report.BTN_GamePadButton5 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_L1; + if (controller_report.BTN_GamePadButton6 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_L2; + if (controller_report.BTN_GamePadButton7 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R1; + if (controller_report.BTN_GamePadButton8 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R2; +} diff --git a/src/hosts/Xbox360Host.cpp b/src/hosts/Xbox360Host.cpp new file mode 100644 index 0000000000..7f67d3ab4e --- /dev/null +++ b/src/hosts/Xbox360Host.cpp @@ -0,0 +1,141 @@ +#include "hosts/Xbox360Host.h" + + +void GamepadUSBHostListener::xbox360_set_led(uint8_t dev_addr, uint8_t instance, uint8_t quadrant) { + uint8_t out[32] = { 0 }; + + memcpy(out, XBOX360_WIRED_LED, sizeof(XBOX360_WIRED_LED)); + out[2] = (quadrant == 0) ? 0 : (quadrant + 5); + bool ret = tuh_xinput_send_report(dev_addr, instance, out, sizeof(XBOX360_WIRED_LED)); + if (ret) { + tuh_xinput_wait_for_tx(dev_addr, instance); + } +} + +void GamepadUSBHostListener::xinput_set_rumble(uint8_t dev_addr, uint8_t instance, uint8_t left, uint8_t right) { + uint8_t out[32] = { 0 }; + uint16_t len = 0; + switch (_controller_type) { + case xinput_type_t::XBOX360: { + memcpy(out, XBOX360_WIRED_RUMBLE, sizeof(XBOX360_WIRED_RUMBLE)); + out[3] = left; + out[4] = right; + len = sizeof(XBOX360_WIRED_RUMBLE); + break; + } + case xinput_type_t::XBOXONE: { + memcpy(out, XBOXONE_RUMBLE, sizeof(XBOXONE_RUMBLE)); + out[8] = left >> 1; // 7-bit (0-127) + out[9] = right >> 1; // 7-bit (0-127) + len = sizeof(XBOXONE_RUMBLE); + break; + } + default: + return; + } + tuh_xinput_wait_for_tx(dev_addr, instance); + bool ret = tuh_xinput_send_report(dev_addr, instance, out, len); + if (ret) { + tuh_xinput_wait_for_tx(dev_addr, instance); + } +} + +void GamepadUSBHostListener::setup_xinput(uint8_t dev_addr, uint8_t instance) { + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + + switch (_controller_type) { + case xinput_type_t::XBOX360: { + uint32_t quadrants = gamepad->auxState.playerID.value; + if (quadrants == 0) + quadrants = 1; + for (uint32_t i = 0; i < quadrants; i++) { + xbox360_set_led(dev_addr, instance, i); + } + + xinput_set_rumble(dev_addr, instance, 0, 0); + break; + } + case xinput_type_t::XBOXONE: { + // implement xbox one init here... currently not supported + break; + } + default: // unsupported + break; + } + tuh_xinput_receive_report(dev_addr, instance); +} + +void GamepadUSBHostListener::update_xinput(uint8_t dev_addr, uint8_t instance) { + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + + static uint8_t last_left_rumble = 0; + static uint8_t last_right_rumble = 0; + + // rumble + gamepad->auxState.haptics.leftActuator.enabled = 1; + gamepad->auxState.haptics.rightActuator.enabled = 1; + uint8_t leftRumble = 0; + uint8_t rightRumble = 0; + if (gamepad->auxState.haptics.leftActuator.active) { + leftRumble = gamepad->auxState.haptics.leftActuator.intensity; + } + if (gamepad->auxState.haptics.rightActuator.active) { + rightRumble = gamepad->auxState.haptics.rightActuator.intensity; + } + + if (leftRumble == last_left_rumble && rightRumble == last_right_rumble) { + return; // no change + } + last_left_rumble = leftRumble; + last_right_rumble = rightRumble; + + xinput_set_rumble(dev_addr, instance, leftRumble, rightRumble); +} + +void GamepadUSBHostListener::process_xbox360(uint8_t const* report, uint16_t len) { + XInputReport controller_report; + + static XInputReport prev_report = { 0 }; + + if (len < sizeof(XInputReport)) { +#if GAMEPAD_HOST_DEBUG + printf("Xbox 360 report too small: %d bytes\n", len); +#endif + return; + } + + memcpy(&controller_report, report, sizeof(controller_report)); + + if (memcmp(&prev_report, &controller_report, sizeof(XInputReport)) == 0) + return; + + _controller_host_state.dpad = 0; + if (controller_report.buttons1 & XBOX_MASK_UP) _controller_host_state.dpad |= GAMEPAD_MASK_UP; + if (controller_report.buttons1 & XBOX_MASK_DOWN) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; + if (controller_report.buttons1 & XBOX_MASK_LEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; + if (controller_report.buttons1 & XBOX_MASK_RIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; + + _controller_host_state.buttons = 0; + if (controller_report.buttons1 & XBOX_MASK_START) _controller_host_state.buttons |= GAMEPAD_MASK_S2; + if (controller_report.buttons1 & XBOX_MASK_BACK) _controller_host_state.buttons |= GAMEPAD_MASK_S1; + if (controller_report.buttons1 & XBOX_MASK_LS) _controller_host_state.buttons |= GAMEPAD_MASK_L3; + if (controller_report.buttons1 & XBOX_MASK_RS) _controller_host_state.buttons |= GAMEPAD_MASK_R3; + if (controller_report.buttons2 & XBOX_MASK_LB) _controller_host_state.buttons |= GAMEPAD_MASK_L1; + if (controller_report.buttons2 & XBOX_MASK_RB) _controller_host_state.buttons |= GAMEPAD_MASK_R1; + if (controller_report.buttons2 & XBOX_MASK_HOME) _controller_host_state.buttons |= GAMEPAD_MASK_A1; + if (controller_report.buttons2 & XBOX_MASK_A) _controller_host_state.buttons |= GAMEPAD_MASK_B1; + if (controller_report.buttons2 & XBOX_MASK_B) _controller_host_state.buttons |= GAMEPAD_MASK_B2; + if (controller_report.buttons2 & XBOX_MASK_X) _controller_host_state.buttons |= GAMEPAD_MASK_B3; + if (controller_report.buttons2 & XBOX_MASK_Y) _controller_host_state.buttons |= GAMEPAD_MASK_B4; + + _controller_host_state.lx = static_cast(controller_report.lx - INT16_MIN); + _controller_host_state.ly = ~static_cast(controller_report.ly - INT16_MIN); + _controller_host_state.rx = static_cast(controller_report.rx - INT16_MIN); + _controller_host_state.ry = ~static_cast(controller_report.ry - INT16_MIN); + + _controller_host_state.lt = controller_report.lt; + _controller_host_state.rt = controller_report.rt; + _controller_host_analog = true; + + prev_report = controller_report; +} From d33d38e58f96d1b868f29ac596144e92502db002 Mon Sep 17 00:00:00 2001 From: Luke A Date: Sun, 21 Jun 2026 13:38:39 -0400 Subject: [PATCH 02/11] Everything should be working up to Switch, Xbox 360/Xbox one is next. Untested! --- CMakeLists.txt | 5 + headers/addons/gamepad_usb_host_listener.h | 1 - .../drivers/p5general/P5GeneralDescriptors.h | 2 +- headers/gphost.h | 6 +- headers/hosts/DrivingForceHost.h | 29 +++ headers/hosts/DualsensePS5Host.h | 56 ++--- headers/hosts/DualshockPS4Host.h | 6 +- headers/hosts/GoogleStadiaHost.h | 18 ++ headers/hosts/SwitchProHost.h | 26 +++ headers/hosts/Ultrastik360Host.h | 17 ++ src/addons/gamepad_usb_host_listener.cpp | 151 +++---------- src/hosts/DrivingForceHost.cpp | 85 ++++++-- src/hosts/DualsensePS5Host.cpp | 120 ++++++----- src/hosts/DualshockPS4Host.cpp | 58 +++-- src/hosts/GoogleStadioHost.cpp | 57 ++++- src/hosts/SwitchProHost.cpp | 204 +++++++++++------- src/hosts/Ultrastik360Host.cpp | 42 +++- 17 files changed, 531 insertions(+), 352 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1cd4a9bba..9166308d6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,7 +304,12 @@ src/animationstation/effects/customthemepressed.cpp src/animationstation/effects/rainbow.cpp src/animationstation/effects/staticcolor.cpp src/animationstation/effects/statictheme.cpp +src/hosts/DualsensePS5Host.cpp src/hosts/DualshockPS4Host.cpp +src/hosts/GoogleStadioHost.cpp +src/hosts/DrivingForceHost.cpp +src/hosts/Ultrastik360Host.cpp +src/hosts/SwitchProHost.cpp ${PROTO_OUTPUT_DIR}/enums.pb.c ${PROTO_OUTPUT_DIR}/config.pb.c ) diff --git a/headers/addons/gamepad_usb_host_listener.h b/headers/addons/gamepad_usb_host_listener.h index 84e294c95f..9eb490b952 100644 --- a/headers/addons/gamepad_usb_host_listener.h +++ b/headers/addons/gamepad_usb_host_listener.h @@ -30,7 +30,6 @@ class GamepadUSBHostListener : public USBListener { virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len); void gamepadFeatureUpdate(); private: - bool _controller_host_mounted; uint8_t _controller_dev_addr; uint8_t _controller_instance; GPHost * _controller_host; diff --git a/headers/drivers/p5general/P5GeneralDescriptors.h b/headers/drivers/p5general/P5GeneralDescriptors.h index 529a66c5f3..99b20c5209 100644 --- a/headers/drivers/p5general/P5GeneralDescriptors.h +++ b/headers/drivers/p5general/P5GeneralDescriptors.h @@ -70,7 +70,7 @@ typedef struct __attribute__((packed)) { uint8_t left_trigger; uint8_t right_trigger; - uint8_t data_7; + uint8_t reportCounter; uint8_t dpad : 4; uint8_t button_west : 1; diff --git a/headers/gphost.h b/headers/gphost.h index cec76f07d0..be5df642b1 100644 --- a/headers/gphost.h +++ b/headers/gphost.h @@ -9,9 +9,7 @@ #include #include "gamepad/GamepadState.h" - -// Forward declare gamepad -class Gamepad; +#include "Gamepad.h" // // GP2040-CE USB Device Class Driver @@ -24,6 +22,8 @@ class GPHost { virtual void update() = 0; virtual void gamepad(Gamepad * gamepad) = 0; virtual void shutdown() = 0; + virtual void set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) = 0; + virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) = 0; protected: GamepadState _controller_host_state; uint8_t _dev_addr; diff --git a/headers/hosts/DrivingForceHost.h b/headers/hosts/DrivingForceHost.h index e69de29bb2..d3206e3b27 100644 --- a/headers/hosts/DrivingForceHost.h +++ b/headers/hosts/DrivingForceHost.h @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Copyright (c) 2026 OpenStickCommunity (gp2040-ce.info) + */ + +#ifndef _DRIVINGFORCEHOST_H_ +#define _DRIVINGFORCEHOST_H_ + +#include + +#include "drivers/ps3/PS3Descriptors.h" + +// +// GP2040-CE USB Host Driver +// +class DrivingForceHost : public GPHost { +public: + static bool match(uint16_t vendor_id, uint16_t product_id); + virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); + virtual void gamepad(Gamepad * gamepad); + virtual void process(uint8_t const* report, uint16_t len); + virtual void update() {} + virtual void shutdown() {} + virtual void set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} + virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} +private: +}; + +#endif diff --git a/headers/hosts/DualsensePS5Host.h b/headers/hosts/DualsensePS5Host.h index 4b575327f7..c8208c68ef 100644 --- a/headers/hosts/DualsensePS5Host.h +++ b/headers/hosts/DualsensePS5Host.h @@ -8,56 +8,24 @@ #include -#include "drivers/ps4/PS4Descriptors.h" -#include "drivers/ps4/PS4Driver.h" - -typedef struct __attribute__((packed)) { - uint8_t reportID; - uint8_t leftStickX; - uint8_t leftStickY; - uint8_t rightStickX; - uint8_t rightStickY; - uint8_t leftTrigger; - uint8_t rightTrigger; - - // 8 bit report counter. - uint8_t reportCounter; - - // 4 bits for the d-pad. - uint8_t dpad : 4; - - // 14 bits for buttons. - uint16_t buttonWest : 1; - uint16_t buttonSouth : 1; - uint16_t buttonEast : 1; - uint16_t buttonNorth : 1; - uint16_t buttonL1 : 1; - uint16_t buttonR1 : 1; - uint16_t buttonL2 : 1; - uint16_t buttonR2 : 1; - uint16_t buttonSelect : 1; - uint16_t buttonStart : 1; - uint16_t buttonL3 : 1; - uint16_t buttonR3 : 1; - uint16_t buttonHome : 1; - uint16_t buttonTouchpad : 1; - uint16_t buttonMicMute : 1; - - uint8_t miscData[54]; -} DSReport; +#include "drivers/p5general/P5GeneralDescriptors.h" // // Dualsense PS5 Controller Host // -class DualsenseHost : public GPHost { +class DualsensePS5Host : public GPHost { public: - virtual bool match(uint8_t vendor_id, uint8_t product_id); - virtual void initialize(); + static bool match(uint16_t vendor_id, uint16_t product_id); + virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void gamepad(Gamepad * gamepad); - virtual void process(uint8_t const* report, uint16_t len); // all these? - virtual void shutdown(); + virtual void process(uint8_t const* report, uint16_t len); + virtual void update() {} + virtual void shutdown() {} + virtual void set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} + virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} private: - bool requiresInit; + P5GenerorReport prevReport; + uint32_t map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max); }; -#endif // _DUALSENSEHOST_H_ +#endif // _DUALSENSEPS5HOST_H_ diff --git a/headers/hosts/DualshockPS4Host.h b/headers/hosts/DualshockPS4Host.h index 1732c89c41..d1f601b9d7 100644 --- a/headers/hosts/DualshockPS4Host.h +++ b/headers/hosts/DualshockPS4Host.h @@ -21,11 +21,13 @@ class DualshockPS4Host : public GPHost { virtual void gamepad(Gamepad * gamepad); virtual void process(uint8_t const* report, uint16_t len); virtual void update(); - virtual void shutdown(); + virtual void shutdown() {} + virtual void set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} + virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len); private: // Controller report processor functions PS4Report prevReport; - bool validDS4HID; + bool validPS4Definition; PS4ControllerConfig ds4Config; uint8_t report_buffer[PS4_ENDPOINT_SIZE]; uint32_t map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max); diff --git a/headers/hosts/GoogleStadiaHost.h b/headers/hosts/GoogleStadiaHost.h index 90ed46d72e..645c7e1e66 100644 --- a/headers/hosts/GoogleStadiaHost.h +++ b/headers/hosts/GoogleStadiaHost.h @@ -45,4 +45,22 @@ typedef struct TU_ATTR_PACKED } google_stadia_report_t; +// +// GP2040-CE USB Host Driver +// +class GoogleStadiaHost : public GPHost { +public: + static bool match(uint16_t vendor_id, uint16_t product_id); + virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); + virtual void gamepad(Gamepad * gamepad); + virtual void process(uint8_t const* report, uint16_t len); + virtual void update() {} + virtual void shutdown() {} + virtual void set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} + virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} +private: + // Controller report processor functions + uint32_t map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max); +}; + #endif diff --git a/headers/hosts/SwitchProHost.h b/headers/hosts/SwitchProHost.h index 6cbb161aab..392256bbd8 100644 --- a/headers/hosts/SwitchProHost.h +++ b/headers/hosts/SwitchProHost.h @@ -8,6 +8,9 @@ #include +#include "drivers/switchpro/SwitchProDescriptors.h" +#include "drivers/switchpro/SwitchProDriver.h" + typedef struct __attribute__((packed)) { uint8_t command; uint8_t counter; @@ -17,4 +20,27 @@ typedef struct __attribute__((packed)) { uint8_t subcommand_args[3]; } SwitchProHostReport; +// +// GP2040-CE USB Host Driver +// +class SwitchProHost : public GPHost { +public: + static bool match(uint16_t vendor_id, uint16_t product_id); + virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); + virtual void gamepad(Gamepad * gamepad); + virtual void process(uint8_t const* report, uint16_t len); + virtual void update(); + virtual void shutdown() {} + virtual void set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} + virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} +private: + void setup_switch_pro(uint8_t const *report, uint16_t len); + uint32_t map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max); + SwitchProReport prevReport; + SwitchOutputSubtypes switchProState; + bool switchProFinished; + uint8_t switchReportCounter; + uint8_t lastSwitchLed; +}; + #endif diff --git a/headers/hosts/Ultrastik360Host.h b/headers/hosts/Ultrastik360Host.h index 8020df79c3..37c83d2329 100644 --- a/headers/hosts/Ultrastik360Host.h +++ b/headers/hosts/Ultrastik360Host.h @@ -35,4 +35,21 @@ typedef struct TU_ATTR_PACKED } ultrastik360_t; +// +// Ultrastik 360 Controller Host +// +class Ultrastik360Host : public GPHost { +public: + static bool match(uint16_t vendor_id, uint16_t product_id); + virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); + virtual void gamepad(Gamepad * gamepad); + virtual void process(uint8_t const* report, uint16_t len); + virtual void update() {} + virtual void shutdown() {} + virtual void set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} + virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} +private: + uint32_t map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max); +}; + #endif diff --git a/src/addons/gamepad_usb_host_listener.cpp b/src/addons/gamepad_usb_host_listener.cpp index da871fdd8f..811cf3f07c 100644 --- a/src/addons/gamepad_usb_host_listener.cpp +++ b/src/addons/gamepad_usb_host_listener.cpp @@ -3,20 +3,19 @@ #include "class/hid/hid.h" #include "class/hid/hid_host.h" -//#include "hosts/DualsensePS5Host.h" +#include "hosts/DualsensePS5Host.h" #include "hosts/DualshockPS4Host.h" - -/* -#include "hosts/DrivingForceHost.h" #include "hosts/GoogleStadiaHost.h" -#include "hosts/SwitchProHost.h" +#include "hosts/DrivingForceHost.h" #include "hosts/UltraStik360Host.h" +#include "hosts/SwitchProHost.h" + +/* #include "hosts/Xbox360Host.h" #include "hosts/XboxOneHost.h" */ void GamepadUSBHostListener::setup() { - _controller_host_mounted = false; _controller_host = nullptr; _controller_dev_addr = 0; _controller_instance = 0; @@ -24,7 +23,7 @@ void GamepadUSBHostListener::setup() { } void GamepadUSBHostListener::gamepadFeatureUpdate() { - if ( _controller_host_mounted == false ) return; + if ( _controller_host == nullptr ) return; // Controller host updates (rumble, LEDs, etc. outside of gamepad input reports) if (getMillis() > _next_update) { @@ -39,9 +38,7 @@ void GamepadUSBHostListener::gamepadFeatureUpdate() { void GamepadUSBHostListener::mount(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) { // Only host one controller at a time - if ( _controller_host_mounted == true ) { - return; - } + if ( _controller_host == nullptr ) return; uint16_t vid = 0, pid = 0; tuh_vid_pid_get(dev_addr, &vid, &pid); @@ -51,25 +48,24 @@ void GamepadUSBHostListener::mount(uint8_t dev_addr, uint8_t instance, uint8_t c _controller_host = nullptr; if ( DualshockPS4Host::match(vid, pid) ) { _controller_host = (GPHost*)(new DualshockPS4Host()); - }/* else if ( DualsensePS5Host::match(vid, pid) ) { + }else if ( DualsensePS5Host::match(vid, pid) ) { _controller_host = new DualsensePS5Host(); - }else if ( GoogleStadiaHost::match(vid, pid) ) { + } else if ( GoogleStadiaHost::match(vid, pid) ) { _controller_host = new GoogleStadiaHost(); - } else if ( SwitchProHost::match(vid, pid) ) { - _controller_host = new SwitchProHost(); } else if ( DrivingForceHost::match(vid, pid) ) { _controller_host = new DrivingForceHost(); } else if ( Ultrastik360Host::match(vid, pid) ) { _controller_host = new Ultrastik360Host(); + } else if ( SwitchProHost::match(vid, pid) ) { + _controller_host = new SwitchProHost(); } else { // Better way to determine if its a generic HID? //if ( GenericHIDHost::match(vid, pid) ) { // do we want this one? //controllerHost = new GenericHIDHost(); - }*/ + } if ( _controller_host != nullptr ) { - _controller_host_mounted = true; _controller_dev_addr = dev_addr; _controller_instance = instance; _controller_host->initialize(dev_addr, instance, vid, pid, desc_report, desc_len); @@ -86,9 +82,7 @@ void GamepadUSBHostListener::mount(uint8_t dev_addr, uint8_t instance, uint8_t c void GamepadUSBHostListener::xmount(uint8_t dev_addr, uint8_t instance, uint8_t controllerType, uint8_t subtype) { // Only host one controller at a time - if ( _controller_host_mounted == true ) { - return; - } + if ( _controller_host == nullptr ) return; uint16_t vid, pid; tuh_vid_pid_get(dev_addr, &vid, &pid); @@ -102,7 +96,6 @@ void GamepadUSBHostListener::xmount(uint8_t dev_addr, uint8_t instance, uint8_t }*/ if ( _controller_host != nullptr ) { - _controller_host_mounted = true; _controller_dev_addr = dev_addr; _controller_instance = instance; _controller_host->initialize(dev_addr, instance, vid, pid, nullptr, 0); @@ -116,117 +109,33 @@ void GamepadUSBHostListener::xmount(uint8_t dev_addr, uint8_t instance, uint8_t } void GamepadUSBHostListener::unmount(uint8_t dev_addr) { - if ( _controller_host_mounted == true ) { - _controller_host->shutdown(); - delete _controller_host; - _controller_host = nullptr; - _controller_host_mounted = false; - } + if ( _controller_host == nullptr ) return; + + _controller_host->shutdown(); + delete _controller_host; + _controller_host = nullptr; } void GamepadUSBHostListener::report_received(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) { - // if a hid device hasn't been mounted - if ( _controller_host_mounted == false ) return; - - if ( _controller_dev_addr == dev_addr && + if ( _controller_host != nullptr && + _controller_dev_addr == dev_addr && _controller_instance == instance ) { _controller_host->process(report, len); } } -/* -void GamepadUSBHostListener::process_ctrlr_report(uint8_t dev_addr, uint8_t const* report, uint16_t len) { -#if GAMEPAD_HOST_DEBUG - //printf("\033[1;0H\nHost (%d):\n", len); - //for (uint8_t i = 0; i < len; i++) { - // printf("%02x ", report[i]); - // if (((i+1) % 16) == 0) printf("\n"); - //} - //printf("----\n"); -#endif - - switch(controller_pid) - { - case DS4_ORG_PRODUCT_ID: // Sony Dualshock 4 controller - case DS4_PRODUCT_ID: // Sony Dualshock 4 controller - case PS4_PRODUCT_ID: // Razer Panthera - case PS4_WHEEL_PRODUCT_ID: // G29 - case 0xB67B: // T-Flight - case 0x00EE: // Hori Minipad - if (isDS4Identified) { - process_ds4(report, len); - } - break; - case 0x0CE6: // DualSense - process_ds(report, len); - break; - case SWITCH_PRO_PRODUCT_ID: // Switch Pro controller - process_switch_pro(report, len); - break; - case 0x9400: // Google Stadia controller - process_stadia(report, len); - break; - - case 0xC294: // Driving Force - if (!isDFInit) setup_df_wheel(); - break; - - case 0xC29A: - process_dfgt(report, len); - break; - - case 0x0510: // pre-2015 Ultrakstik 360 - case 0x0511: // Ultrakstik 360 - process_ultrastik360(report, len); - break; - default: - break; - } -} - -// this is primarily for xinput updates as the controller refuses the send unnecessary -// data, so update wouldn't be called otherwise. but works for every controller and -// provides a more consistent way of quick rumble updates -void GamepadUSBHostListener::update_ctrlr() { - switch (_controller_type) { - case xinput_type_t::XBOX360: - update_xinput(_controller_dev_addr, _controller_instance); - break; - case xinput_type_t::UNKNOWN: - switch(controller_pid) - { - case DS4_ORG_PRODUCT_ID: // Sony Dualshock 4 controller - case DS4_PRODUCT_ID: // Sony Dualshock 4 controller - case PS4_PRODUCT_ID: // Razer Panthera - case PS4_WHEEL_PRODUCT_ID: // G29 - case 0xB67B: // T-Flight - case 0x00EE: // Hori Minipad - if (isDS4Identified) { - update_ds4(); - } - break; - case SWITCH_PRO_PRODUCT_ID: // Switch Pro controller - update_switch_pro(); - break; - default: - break; - } - default: - break; - } -} - - -bool GamepadUSBHostListener::host_get_report(uint8_t report_id, void* report, uint16_t len) { - return tuh_hid_get_report(_controller_dev_addr, _controller_instance, report_id, HID_REPORT_TYPE_FEATURE, report, len); -} - -bool GamepadUSBHostListener::host_set_report(uint8_t report_id, void* report, uint16_t len) { - return tuh_hid_set_report(_controller_dev_addr, _controller_instance, report_id, HID_REPORT_TYPE_FEATURE, report, len); -} -*/ void GamepadUSBHostListener::set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) { + if ( _controller_host != nullptr && + _controller_dev_addr == dev_addr && + _controller_instance == instance ) { + _controller_host->set_report_complete(dev_addr, instance, report_id, report_type, len); + } } void GamepadUSBHostListener::get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) { + if ( _controller_host != nullptr && + _controller_dev_addr == dev_addr && + _controller_instance == instance ) { + _controller_host->get_report_complete(dev_addr, instance, report_id, report_type, len); + } } diff --git a/src/hosts/DrivingForceHost.cpp b/src/hosts/DrivingForceHost.cpp index 4e993c62cc..5fb1f7bb00 100644 --- a/src/hosts/DrivingForceHost.cpp +++ b/src/hosts/DrivingForceHost.cpp @@ -1,31 +1,40 @@ #include "hosts/DrivingForceHost.h" +#include "tusb_config.h" +#include "tusb.h" +#include "class/hid/hid.h" +#include "device/usbd_pvt.h" -/* - // Logitech G29 Driving Force - if ( vendor_id == ) { +bool DrivingForceHost::match(uint16_t vendor_id, uint16_t product_id) { + if ( vendor_id == 0x46d) { switch(product_id) { - case 0xC260: + case 0xC294: // compatible mode + case 0xC29A: return true; } } + return false; +} - // Thrustmaster T.Flight Racing Wheel - if ( vendor_id == 0x44F) { - switch(product_id) { - case 0xB67B: - return true; - } - } -*/ +void DrivingForceHost::initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { + // Setup our vars + _dev_addr = dev_addr; + _instance = instance; + _vendor_id = vendor_id; + _product_id = product_id; -void DrivingForceHost::intialize() { - // send commands to see if can be reset to Driving Force GT mode for more compatibility - uint8_t command[8] = {0xF8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00}; - uint16_t commandSize = sizeof(command); + _controller_host_state.buttons = 0; + _controller_host_state.dpad = 0; + _controller_host_state.lx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ly = GAMEPAD_JOYSTICK_MID; + _controller_host_state.rx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ry = GAMEPAD_JOYSTICK_MID; - if (tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, command, commandSize)) { - isDFInit = true; + if ( vendor_id == 0x46d && product_id == 0xC294 ) { + // send commands to see if can be reset to Driving Force GT mode for more compatibility + uint8_t command[8] = {0xF8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00}; + uint16_t commandSize = sizeof(command); + tuh_hid_send_report(dev_addr, instance, 0, command, commandSize); } } @@ -45,4 +54,44 @@ void DrivingForceHost::process(uint8_t const* report, uint16_t len) { //printf("\033[4;0HGas: %3d", ps3Report.wheel.gasPedal); //printf("\033[5;0HBrake: %3d", ps3Report.wheel.brakePedal); #endif +/* + ps3Report.wheel.buttonNorth; + ps3Report.wheel.buttonEast; + ps3Report.wheel.buttonSouth; + ps3Report.wheel.buttonWest; + + ps3Report.wheel.buttonL2; + ps3Report.wheel.buttonR2; + ps3Report.wheel.buttonL1; + ps3Report.wheel.buttonR1; + + ps3Report.wheel.buttonStart; + ps3Report.wheel.buttonSelect; + ps3Report.wheel.buttonL3; + ps3Report.wheel.buttonR3; + + ps3Report.wheel.buttonPS; + ps3Report.wheel.buttonTP; + ps3Report.wheel.dpadDirection; + + ps3Report.wheel.steeringWheel; + ps3Report.wheel.brakePedal; + ps3Report.wheel.gasPedal; + ps3Report.wheel.clutchPedal; +*/ +} + +void DrivingForceHost::gamepad(Gamepad * gamepad) { + // Override the Gamepad + gamepad->hasAnalogTriggers = true; + gamepad->hasLeftAnalogStick = true; + gamepad->hasRightAnalogStick = true; + gamepad->state.dpad |= _controller_host_state.dpad; + gamepad->state.buttons |= _controller_host_state.buttons; + gamepad->state.lx = _controller_host_state.lx; + gamepad->state.ly = _controller_host_state.ly; + gamepad->state.rx = _controller_host_state.rx; + gamepad->state.ry = _controller_host_state.ry; + gamepad->state.rt = _controller_host_state.rt; + gamepad->state.lt = _controller_host_state.lt; } \ No newline at end of file diff --git a/src/hosts/DualsensePS5Host.cpp b/src/hosts/DualsensePS5Host.cpp index b4e32e15ce..d72d6ed9ea 100644 --- a/src/hosts/DualsensePS5Host.cpp +++ b/src/hosts/DualsensePS5Host.cpp @@ -1,69 +1,93 @@ #include "hosts/DualsensePS5Host.h" -void DualsensePS5Host::initialize() { - requiresInit = false; -} - -bool DualsensePS5Host::match(uint8_t vendor_id, uint8_t product_id) { +bool DualsensePS5Host::match(uint16_t vendor_id, uint16_t product_id) { // Playstation 5 Dualsense - switch(product_id) { - case 0x0CE6: // DualSense - return true; - default: - break; + if ( vendor_id == 0x054c) { + switch(product_id) { + case 0x0CE6: // DualSense + return true; + } } return false; } +void DualsensePS5Host::initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { + // Setup our vars + _dev_addr = dev_addr; + _instance = instance; + _vendor_id = vendor_id; + _product_id = product_id; -void DualsensePS5Host::process(uint8_t const* report, uint16_t len) { - DSReport controller_report; - - // previous report used to compare for changes - static DSReport prev_ds_report = { 0 }; + _controller_host_state.buttons = 0; + _controller_host_state.dpad = 0; + _controller_host_state.lx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ly = GAMEPAD_JOYSTICK_MID; + _controller_host_state.rx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ry = GAMEPAD_JOYSTICK_MID; - uint8_t const report_id = report[0]; + memset(&prevReport, 0, sizeof(P5GenerorReport)); +} - if (report_id == 1) { +void DualsensePS5Host::process(uint8_t const* report, uint16_t len) { + P5GenerorReport controller_report; + if (report[0] == 1) { memcpy(&controller_report, report, sizeof(controller_report)); - if ( prev_ds_report.reportCounter != controller_report.reportCounter ) { - _controller_host_state.lx = map(controller_report.leftStickX, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ly = map(controller_report.leftStickY, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.rx = map(controller_report.rightStickX,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ry = map(controller_report.rightStickY,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.lt = controller_report.leftTrigger; - _controller_host_state.rt = controller_report.rightTrigger; - _controller_host_analog = true; + if ( prevReport.reportCounter != controller_report.reportCounter ) { + _controller_host_state.lx = map(controller_report.left_stick_x, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ly = map(controller_report.left_stick_y, 0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.rx = map(controller_report.right_stick_x,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ry = map(controller_report.right_stick_y,0,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.lt = controller_report.left_trigger; + _controller_host_state.rt = controller_report.right_trigger; _controller_host_state.buttons = 0; - if (controller_report.buttonTouchpad) _controller_host_state.buttons |= GAMEPAD_MASK_A2; - if (controller_report.buttonSelect) _controller_host_state.buttons |= GAMEPAD_MASK_S1; - if (controller_report.buttonR3) _controller_host_state.buttons |= GAMEPAD_MASK_R3; - if (controller_report.buttonL3) _controller_host_state.buttons |= GAMEPAD_MASK_L3; - if (controller_report.buttonHome) _controller_host_state.buttons |= GAMEPAD_MASK_A1; - if (controller_report.buttonStart) _controller_host_state.buttons |= GAMEPAD_MASK_S2; - if (controller_report.buttonR1) _controller_host_state.buttons |= GAMEPAD_MASK_R1; - if (controller_report.buttonL1) _controller_host_state.buttons |= GAMEPAD_MASK_L1; - if (controller_report.buttonNorth) _controller_host_state.buttons |= GAMEPAD_MASK_B4; - if (controller_report.buttonEast) _controller_host_state.buttons |= GAMEPAD_MASK_B2; - if (controller_report.buttonSouth) _controller_host_state.buttons |= GAMEPAD_MASK_B1; - if (controller_report.buttonWest) _controller_host_state.buttons |= GAMEPAD_MASK_B3; - if (controller_report.buttonR2) _controller_host_state.buttons |= GAMEPAD_MASK_R2; - if (controller_report.buttonL2) _controller_host_state.buttons |= GAMEPAD_MASK_L2; + if (controller_report.button_touchpad) _controller_host_state.buttons |= GAMEPAD_MASK_A2; + if (controller_report.button_select) _controller_host_state.buttons |= GAMEPAD_MASK_S1; + if (controller_report.button_r3) _controller_host_state.buttons |= GAMEPAD_MASK_R3; + if (controller_report.button_l3) _controller_host_state.buttons |= GAMEPAD_MASK_L3; + if (controller_report.button_home) _controller_host_state.buttons |= GAMEPAD_MASK_A1; + if (controller_report.button_start) _controller_host_state.buttons |= GAMEPAD_MASK_S2; + if (controller_report.button_r1) _controller_host_state.buttons |= GAMEPAD_MASK_R1; + if (controller_report.button_l1) _controller_host_state.buttons |= GAMEPAD_MASK_L1; + if (controller_report.button_north) _controller_host_state.buttons |= GAMEPAD_MASK_B4; + if (controller_report.button_east) _controller_host_state.buttons |= GAMEPAD_MASK_B2; + if (controller_report.button_south) _controller_host_state.buttons |= GAMEPAD_MASK_B1; + if (controller_report.button_west) _controller_host_state.buttons |= GAMEPAD_MASK_B3; + if (controller_report.button_r2) _controller_host_state.buttons |= GAMEPAD_MASK_R2; + if (controller_report.button_l2) _controller_host_state.buttons |= GAMEPAD_MASK_L2; _controller_host_state.dpad = 0; - if (controller_report.dpad == PS4_HAT_UP) _controller_host_state.dpad |= GAMEPAD_MASK_UP; - if (controller_report.dpad == PS4_HAT_UPRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_UP | GAMEPAD_MASK_RIGHT; - if (controller_report.dpad == PS4_HAT_RIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; - if (controller_report.dpad == PS4_HAT_DOWNRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT | GAMEPAD_MASK_DOWN; - if (controller_report.dpad == PS4_HAT_DOWN) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; - if (controller_report.dpad == PS4_HAT_DOWNLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN | GAMEPAD_MASK_LEFT; - if (controller_report.dpad == PS4_HAT_LEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; - if (controller_report.dpad == PS4_HAT_UPLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT | GAMEPAD_MASK_UP; + if (controller_report.dpad == P5GENERAL_HAT_UP) _controller_host_state.dpad |= GAMEPAD_MASK_UP; + if (controller_report.dpad == P5GENERAL_HAT_UPRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_UP | GAMEPAD_MASK_RIGHT; + if (controller_report.dpad == P5GENERAL_HAT_RIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT; + if (controller_report.dpad == P5GENERAL_HAT_DOWNRIGHT) _controller_host_state.dpad |= GAMEPAD_MASK_RIGHT | GAMEPAD_MASK_DOWN; + if (controller_report.dpad == P5GENERAL_HAT_DOWN) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN; + if (controller_report.dpad == P5GENERAL_HAT_DOWNLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_DOWN | GAMEPAD_MASK_LEFT; + if (controller_report.dpad == P5GENERAL_HAT_LEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; + if (controller_report.dpad == P5GENERAL_HAT_UPLEFT) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT | GAMEPAD_MASK_UP; } } - prev_ds_report = controller_report; + memcpy(&prevReport, &controller_report, sizeof(P5GenerorReport)); +} + +void DualsensePS5Host::gamepad(Gamepad * gamepad) { + // Override the Gamepad + gamepad->hasAnalogTriggers = true; + gamepad->hasLeftAnalogStick = true; + gamepad->hasRightAnalogStick = true; + gamepad->state.dpad |= _controller_host_state.dpad; + gamepad->state.buttons |= _controller_host_state.buttons; + gamepad->state.lx = _controller_host_state.lx; + gamepad->state.ly = _controller_host_state.ly; + gamepad->state.rx = _controller_host_state.rx; + gamepad->state.ry = _controller_host_state.ry; + gamepad->state.rt = _controller_host_state.rt; + gamepad->state.lt = _controller_host_state.lt; +} + +uint32_t DualsensePS5Host::map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } diff --git a/src/hosts/DualshockPS4Host.cpp b/src/hosts/DualshockPS4Host.cpp index 8131498d79..838cfed985 100644 --- a/src/hosts/DualshockPS4Host.cpp +++ b/src/hosts/DualshockPS4Host.cpp @@ -30,6 +30,22 @@ bool DualshockPS4Host::match(uint16_t vendor_id, uint16_t product_id) { } } + // Logitech G29 + if ( vendor_id == 0x46d) { + switch(product_id) { + case 0xC260: + return true; + } + } + + // Thrustmaster T.Flight Racing Wheel + if ( vendor_id == 0x044F) { + switch(product_id) { + case 0xB67B: + return true; + } + } + return false; } @@ -50,7 +66,7 @@ void DualshockPS4Host::initialize(uint8_t dev_addr, uint8_t instance, uint16_t v memset(&prevReport, 0, sizeof(PS4Report)); - validDS4HID = false; // check the report def + validPS4Definition = false; // check the report def // Find the report for PS4 definition if this controller has one tuh_hid_report_info_t report_info[4]; @@ -64,29 +80,14 @@ void DualshockPS4Host::initialize(uint8_t dev_addr, uint8_t instance, uint16_t v memset(report_buffer, 0, PS4_ENDPOINT_SIZE); report_buffer[0] = PS4AuthReport::PS4_DEFINITION; tuh_hid_get_report(dev_addr, instance, PS4AuthReport::PS4_DEFINITION, HID_REPORT_TYPE_FEATURE, report_buffer, 48); - memcpy(&ds4Config, report_buffer+1, sizeof(PS4ControllerConfig)); - if ((ds4Config.hidUsage == 0x2721) || (ds4Config.hidUsage == 0x2127)) { - validDS4HID = true; - #if GAMEPAD_HOST_DEBUG - //printf("PS4 controller details\n"); - //printf("----------------------\n"); - //printf("enableController: %d\n", ds4Config.features.enableController); - //printf("enableMotion: %d\n", ds4Config.features.enableMotion); - //printf("enableLED: %d\n", ds4Config.features.enableLED); - //printf("enableRumble: %d\n", ds4Config.features.enableRumble); - //printf("enableAnalog: %d\n", ds4Config.features.enableAnalog); - //printf("enableUnknown0: %d\n", ds4Config.features.enableUnknown0); - //printf("enableTouchpad: %d\n", ds4Config.features.enableTouchpad); - //printf("enableUnknown1: %d\n", ds4Config.features.enableUnknown1); - #endif - } - break; } } } void DualshockPS4Host::update() { + if ( validPS4Definition == false ) return; + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); PS4FeatureOutputReport controller_output; // this might have to be static memset(&controller_output, 0, sizeof(controller_output)); @@ -114,7 +115,6 @@ void DualshockPS4Host::update() { tuh_hid_send_report(_dev_addr, _instance, PS4AuthReport::PS4_SET_FEATURE_STATE, (uint8_t*)report+1, report_size); } - void DualshockPS4Host::process(uint8_t const* report, uint16_t len) { PS4Report controller_report; @@ -177,11 +177,25 @@ void DualshockPS4Host::gamepad(Gamepad * gamepad) { gamepad->state.lt = _controller_host_state.lt; } -void DualshockPS4Host::shutdown() { +void DualshockPS4Host::get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) { + memcpy(&ds4Config, report_buffer+1, sizeof(PS4ControllerConfig)); + if ((ds4Config.hidUsage == 0x2721) || (ds4Config.hidUsage == 0x2127)) { + validPS4Definition = true; + #if GAMEPAD_HOST_DEBUG + //printf("PS4 controller details\n"); + //printf("----------------------\n"); + //printf("enableController: %d\n", ds4Config.features.enableController); + //printf("enableMotion: %d\n", ds4Config.features.enableMotion); + //printf("enableLED: %d\n", ds4Config.features.enableLED); + //printf("enableRumble: %d\n", ds4Config.features.enableRumble); + //printf("enableAnalog: %d\n", ds4Config.features.enableAnalog); + //printf("enableUnknown0: %d\n", ds4Config.features.enableUnknown0); + //printf("enableTouchpad: %d\n", ds4Config.features.enableTouchpad); + //printf("enableUnknown1: %d\n", ds4Config.features.enableUnknown1); + #endif + } } - - uint32_t DualshockPS4Host::map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } diff --git a/src/hosts/GoogleStadioHost.cpp b/src/hosts/GoogleStadioHost.cpp index 967103fd10..5bdcc1644c 100644 --- a/src/hosts/GoogleStadioHost.cpp +++ b/src/hosts/GoogleStadioHost.cpp @@ -1,17 +1,43 @@ #include "hosts/GoogleStadiaHost.h" -void GoogleStadioHost::process(uint8_t const *report, uint16_t len) { +// Static match function +bool GoogleStadiaHost::match(uint16_t vendor_id, uint16_t product_id) { + // Google Stadia + if ( vendor_id == 0x18D1 ) { + switch(product_id) { + case 0x9400: + return true; + } + } + return false; +} + +void GoogleStadiaHost::initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { + // Setup our vars + _dev_addr = dev_addr; + _instance = instance; + _vendor_id = vendor_id; + _product_id = product_id; + + _controller_host_state.buttons = 0; + _controller_host_state.dpad = 0; + _controller_host_state.lx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ly = GAMEPAD_JOYSTICK_MID; + _controller_host_state.rx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ry = GAMEPAD_JOYSTICK_MID; +} + +void GoogleStadiaHost::process(uint8_t const *report, uint16_t len) { google_stadia_report_t controller_report; memcpy(&controller_report, report, sizeof(controller_report)); - _controller_host_state.lx = map(controller_report.GD_GamePadPointerX ,1,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ly = map(controller_report.GD_GamePadPointerY,1 ,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.rx = map(controller_report.GD_GamePadPointerZ,1 ,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_state.ry = map(controller_report.GD_GamePadPointerRz,1 ,255,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + _controller_host_state.lx = map(controller_report.GD_GamePadPointerX, 1, 255, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ly = map(controller_report.GD_GamePadPointerY, 1, 255, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); + _controller_host_state.rx = map(controller_report.GD_GamePadPointerZ, 1, 255, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); + _controller_host_state.ry = map(controller_report.GD_GamePadPointerRz, 1, 255, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); _controller_host_state.lt = controller_report.SIM_GamePadBrake; _controller_host_state.rt = controller_report.SIM_GamePadAccelerator; - _controller_host_analog = true; if (controller_report.BTN_GamePadButton18 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_A2; if (controller_report.BTN_GamePadButton17 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_A3; @@ -38,3 +64,22 @@ void GoogleStadioHost::process(uint8_t const *report, uint16_t len) { if (controller_report.GD_GamePadHatSwitch == 6) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT; if (controller_report.GD_GamePadHatSwitch == 7) _controller_host_state.dpad |= GAMEPAD_MASK_LEFT | GAMEPAD_MASK_UP; } + +void GoogleStadiaHost::gamepad(Gamepad * gamepad) { + // Override the Gamepad + gamepad->hasAnalogTriggers = true; + gamepad->hasLeftAnalogStick = true; + gamepad->hasRightAnalogStick = true; + gamepad->state.dpad |= _controller_host_state.dpad; + gamepad->state.buttons |= _controller_host_state.buttons; + gamepad->state.lx = _controller_host_state.lx; + gamepad->state.ly = _controller_host_state.ly; + gamepad->state.rx = _controller_host_state.rx; + gamepad->state.ry = _controller_host_state.ry; + gamepad->state.rt = _controller_host_state.rt; + gamepad->state.lt = _controller_host_state.lt; +} + +uint32_t GoogleStadiaHost::map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} diff --git a/src/hosts/SwitchProHost.cpp b/src/hosts/SwitchProHost.cpp index d8f7f7ef85..0b109c8036 100644 --- a/src/hosts/SwitchProHost.cpp +++ b/src/hosts/SwitchProHost.cpp @@ -1,67 +1,44 @@ -#include "SwitchProHost.h" +#include "hosts/SwitchProHost.h" +#include "tusb_config.h" +#include "tusb.h" +#include "class/hid/hid.h" +#include "device/usbd_pvt.h" +#include "storagemanager.h" - -#undef GAMEPAD_HOST_DEBUG +// Match +bool SwitchProHost::match(uint16_t vendor_id, uint16_t product_id) { + // Nintendo + if ( vendor_id == 0x057E) { + switch(product_id) { + case 0x2009: + return true; + } + } + + return false; +} // init phase // https://github.com/wiredopposite/OGX-Mini/blob/ccccf6651b54bcca3c94b5d4e73e8f2796fc8c05/Firmware/RP2040/src/USBHost/HostDriver/SwitchPro/SwitchPro.cpp#L23 -void SwitchProHost::initialize(uint8_t vendor_id, uint8_t product_id, uint8_t const* desc_report, uint16_t desc_len) { - SwitchProHostReport out_report{ - .rumble_l = {0x00, 0x01, 0x40, 0x40}, // default rumble states - .rumble_r = {0x00, 0x01, 0x40, 0x40}, - }; - uint8_t report_size = 10; // no subcommand - -#ifdef GAMEPAD_HOST_DEBUG - printf("Switch Pro init state: %d\n.", switchProState); -#endif - - switch (switchProState) - { - case SwitchOutputSubtypes::IDENTIFY: { - if (len < 10 - || report[0] != SwitchReportID::REPORT_USB_INPUT_81 - || report[1] != SwitchOutputSubtypes::IDENTIFY) { - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &SWITCH_INIT_REPORT, sizeof(SWITCH_INIT_REPORT)); - break; - } - - out_report.command = SwitchReportID::REPORT_CONFIGURATION; - out_report.counter = SwitchOutputSubtypes::HANDSHAKE; - get_next_switch_counter(); // skip counter - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &out_report, report_size); - // might turn up the home LED as well - switchProState = SwitchOutputSubtypes::DISABLE_USB_TIMEOUT; - break; - } - case SwitchOutputSubtypes::DISABLE_USB_TIMEOUT: { - if (len < 2 || - (report[0] != SwitchReportID::REPORT_USB_INPUT_81 - && report[0] != SwitchReportID::REPORT_OUTPUT_30)) { - // reset - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &SWITCH_INIT_REPORT, sizeof(SWITCH_INIT_REPORT)); - switchProState = SwitchOutputSubtypes::IDENTIFY; - break; - } - out_report.command = SwitchReportID::REPORT_CONFIGURATION; - out_report.counter = SwitchOutputSubtypes::DISABLE_USB_TIMEOUT; - get_next_switch_counter(); // skip counter - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &out_report, report_size); - switchProState = SwitchOutputSubtypes::IDENTIFY; // done - switchProFinished = true; -#ifdef GAMEPAD_HOST_DEBUG - printf("Switch Pro controller initialized\n."); -#endif - Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); - gamepad->auxState.playerID.enabled = true; - break; - } +void SwitchProHost::initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { + // Setup our vars + _dev_addr = dev_addr; + _instance = instance; + _vendor_id = vendor_id; + _product_id = product_id; - default: - switchProState = SwitchOutputSubtypes::IDENTIFY; // reset - switchProFinished = false; - break; - } + _controller_host_state.buttons = 0; + _controller_host_state.dpad = 0; + _controller_host_state.lx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ly = GAMEPAD_JOYSTICK_MID; + _controller_host_state.rx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ry = GAMEPAD_JOYSTICK_MID; + + switchProFinished = false; + switchReportCounter = 0; + switchProState = SwitchOutputSubtypes::IDENTIFY; + lastSwitchLed = 0; + memset(&prevReport, 0, sizeof(SwitchProReport)); } @@ -73,7 +50,7 @@ void SwitchProHost::update() SwitchProHostReport out_report{ .command = SwitchCommands::SPI_READ, - .counter = get_next_switch_counter(), + .counter = switchReportCounter++, .rumble_l = {0x00, 0x01, 0x40, 0x40}, // default rumble states .rumble_r = {0x00, 0x01, 0x40, 0x40}, }; @@ -117,7 +94,7 @@ void SwitchProHost::update() if (shifted != lastSwitchLed) { SwitchProHostReport led_out_report{ .command = SwitchOutputSubtypes::IDENTIFY, - .counter = get_next_switch_counter(), + .counter = switchReportCounter++, .rumble_l = {0x00, 0x01, 0x40, 0x40}, .rumble_r = {0x00, 0x01, 0x40, 0x40}, .subcommand = SwitchCommands::SET_PLAYER_LIGHTS, @@ -125,28 +102,18 @@ void SwitchProHost::update() }; uint8_t report_size = 12; // 10 + 2 for subcommand - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &led_out_report, report_size); + tuh_hid_send_report(_dev_addr, _instance, 0, &led_out_report, report_size); } lastSwitchLed = shifted; } - tuh_hid_send_report(_controller_dev_addr, _controller_instance, 0, &out_report, report_size); + tuh_hid_send_report(_dev_addr, _instance, 0, &out_report, report_size); } -uint8_t SwitchProHost::get_next_switch_counter() -{ - if (switchReportCounter < 255) { - switchReportCounter++; - } else { - switchReportCounter = 0; - } - return switchReportCounter; -} - - void SwitchProHost::process(uint8_t const* report, uint16_t len) { -{ - if (len == 0) return; + if (len == 0) + return; + if (!switchProFinished) { #ifdef GAMEPAD_HOST_DEBUG printf("received report id %x during initialization\n", report[0]); @@ -157,8 +124,6 @@ void SwitchProHost::process(uint8_t const* report, uint16_t len) { SwitchProReport controller_report; - static SwitchProReport prev_report = { 0 }; - if (len < sizeof(SwitchProReport)) { #ifdef GAMEPAD_HOST_DEBUG printf("ignoring report with len %d (%x)...\n", len, report[0]); @@ -173,7 +138,7 @@ void SwitchProHost::process(uint8_t const* report, uint16_t len) { } memcpy(&controller_report, report, sizeof(controller_report)); - if (memcmp(&prev_report, &controller_report, sizeof(SwitchProReport)) == 0) + if (memcmp(&prevReport, &controller_report, sizeof(SwitchProReport)) == 0) return; _controller_host_state.dpad = 0; @@ -207,8 +172,85 @@ void SwitchProHost::process(uint8_t const* report, uint16_t len) { _controller_host_state.ly = map(ly12, 0, 4095, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); _controller_host_state.rx = map(rx12, 0, 4095, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); _controller_host_state.ry = map(ry12, 0, 4095, GAMEPAD_JOYSTICK_MIN, GAMEPAD_JOYSTICK_MAX); - _controller_host_analog = false; - prev_report = controller_report; + memcpy(&prevReport, &controller_report, sizeof(SwitchProReport)); +} + + +// init phase +// https://github.com/wiredopposite/OGX-Mini/blob/ccccf6651b54bcca3c94b5d4e73e8f2796fc8c05/Firmware/RP2040/src/USBHost/HostDriver/SwitchPro/SwitchPro.cpp#L23 +void SwitchProHost::setup_switch_pro(uint8_t const *report, uint16_t len) { + SwitchProHostReport out_report{ + .rumble_l = {0x00, 0x01, 0x40, 0x40}, // default rumble states + .rumble_r = {0x00, 0x01, 0x40, 0x40}, + }; + uint8_t report_size = 10; // no subcommand + +#ifdef GAMEPAD_HOST_DEBUG + printf("Switch Pro init state: %d\n.", switchProState); +#endif + + switch (switchProState) + { + case SwitchOutputSubtypes::IDENTIFY: { + if (len < 10 + || report[0] != SwitchReportID::REPORT_USB_INPUT_81 + || report[1] != SwitchOutputSubtypes::IDENTIFY) { + tuh_hid_send_report(_dev_addr, _instance, 0, &SWITCH_INIT_REPORT, sizeof(SWITCH_INIT_REPORT)); + break; + } + + out_report.command = SwitchReportID::REPORT_CONFIGURATION; + out_report.counter = SwitchOutputSubtypes::HANDSHAKE; + switchReportCounter++; // skip counter + tuh_hid_send_report(_dev_addr, _instance, 0, &out_report, report_size); + // might turn up the home LED as well + switchProState = SwitchOutputSubtypes::DISABLE_USB_TIMEOUT; + break; + } + case SwitchOutputSubtypes::DISABLE_USB_TIMEOUT: { + if (len < 2 || + (report[0] != SwitchReportID::REPORT_USB_INPUT_81 + && report[0] != SwitchReportID::REPORT_OUTPUT_30)) { + // reset + tuh_hid_send_report(_dev_addr, _instance, 0, &SWITCH_INIT_REPORT, sizeof(SWITCH_INIT_REPORT)); + switchProState = SwitchOutputSubtypes::IDENTIFY; + break; + } + out_report.command = SwitchReportID::REPORT_CONFIGURATION; + out_report.counter = SwitchOutputSubtypes::DISABLE_USB_TIMEOUT; + switchReportCounter++; // skip counter + tuh_hid_send_report(_dev_addr, _instance, 0, &out_report, report_size); + switchProState = SwitchOutputSubtypes::IDENTIFY; // done + switchProFinished = true; +#ifdef GAMEPAD_HOST_DEBUG + printf("Switch Pro controller initialized\n."); +#endif + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + gamepad->auxState.playerID.enabled = true; + break; + } + + default: + switchProState = SwitchOutputSubtypes::IDENTIFY; // reset + switchProFinished = false; + break; + } +} + +void SwitchProHost::gamepad(Gamepad * gamepad) { + // Override the Gamepad + gamepad->hasLeftAnalogStick = true; + gamepad->hasRightAnalogStick = true; + gamepad->state.dpad |= _controller_host_state.dpad; + gamepad->state.buttons |= _controller_host_state.buttons; + gamepad->state.lx = _controller_host_state.lx; + gamepad->state.ly = _controller_host_state.ly; + gamepad->state.rx = _controller_host_state.rx; + gamepad->state.ry = _controller_host_state.ry; +} + +uint32_t SwitchProHost::map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } diff --git a/src/hosts/Ultrastik360Host.cpp b/src/hosts/Ultrastik360Host.cpp index ec438d5d67..581cce2633 100644 --- a/src/hosts/Ultrastik360Host.cpp +++ b/src/hosts/Ultrastik360Host.cpp @@ -1,15 +1,35 @@ #include "hosts/Ultrastik360Host.h" -void Ultrastik360Host::process(uint8_t const* report, uint16_t len) { +bool Ultrastik360Host::match(uint16_t vendor_id, uint16_t product_id) { + // Ultrastik 360 + // if ( vendor_id == 0x054c) { // ask community what this is + switch(product_id) { + case 0x0510: // pre-2015 + case 0x0511: + return true; + } + //} + + return false; +} - ultrastik360_t controller_report; +void Ultrastik360Host::initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { + // Setup our vars + _dev_addr = dev_addr; + _instance = instance; + _vendor_id = vendor_id; + _product_id = product_id; - memcpy(&controller_report, report, sizeof(controller_report)); + _controller_host_state.buttons = 0; + _controller_host_state.lx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ly = GAMEPAD_JOYSTICK_MID; +} +void Ultrastik360Host::process(uint8_t const* report, uint16_t len) { + ultrastik360_t controller_report; + memcpy(&controller_report, report, sizeof(controller_report)); _controller_host_state.lx = map(controller_report.GD_GamePadPointerX, 0, 255, GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); _controller_host_state.ly = map(controller_report.GD_GamePadPointerY, 0, 255, GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); - _controller_host_analog = true; - if (controller_report.BTN_GamePadButton1 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B1; if (controller_report.BTN_GamePadButton2 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B2; if (controller_report.BTN_GamePadButton3 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_B3; @@ -19,3 +39,15 @@ void Ultrastik360Host::process(uint8_t const* report, uint16_t len) { if (controller_report.BTN_GamePadButton7 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R1; if (controller_report.BTN_GamePadButton8 == 1) _controller_host_state.buttons |= GAMEPAD_MASK_R2; } + +void Ultrastik360Host::gamepad(Gamepad * gamepad) { + // Override the Gamepad + gamepad->hasLeftAnalogStick = true; + gamepad->state.buttons |= _controller_host_state.buttons; + gamepad->state.lx = _controller_host_state.lx; + gamepad->state.ly = _controller_host_state.ly; +} + +uint32_t Ultrastik360Host::map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} From da0419233b4ebfeb8bc143244d1a171f4084d7aa Mon Sep 17 00:00:00 2001 From: Luke A Date: Sun, 21 Jun 2026 13:55:28 -0400 Subject: [PATCH 03/11] Xbox 360 is in, this should match everything we have currently --- CMakeLists.txt | 1 + headers/hosts/Xbox360Host.h | 34 ++++++ src/addons/gamepad_usb_host_listener.cpp | 11 +- src/hosts/Xbox360Host.cpp | 149 +++++++++++++---------- 4 files changed, 124 insertions(+), 71 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9166308d6f..a8f3e87a03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -310,6 +310,7 @@ src/hosts/GoogleStadioHost.cpp src/hosts/DrivingForceHost.cpp src/hosts/Ultrastik360Host.cpp src/hosts/SwitchProHost.cpp +src/hosts/Xbox360Host.cpp ${PROTO_OUTPUT_DIR}/enums.pb.c ${PROTO_OUTPUT_DIR}/config.pb.c ) diff --git a/headers/hosts/Xbox360Host.h b/headers/hosts/Xbox360Host.h index e69de29bb2..b815d19301 100644 --- a/headers/hosts/Xbox360Host.h +++ b/headers/hosts/Xbox360Host.h @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Copyright (c) 2026 OpenStickCommunity (gp2040-ce.info) + */ + +#ifndef _XBOX360HOST_H_ +#define _XBOX360HOST_H_ + +#include + +#include "drivers/xinput/XInputDescriptors.h" + +// +// Xbox 360 Controller Host +// +class Xbox360Host : public GPHost { +public: + static bool match(uint16_t vendor_id, uint16_t product_id); + virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); + virtual void gamepad(Gamepad * gamepad); + virtual void process(uint8_t const* report, uint16_t len); + virtual void update(); + virtual void shutdown() {} + virtual void set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} + virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} +private: + void xbox360_set_led(uint8_t quadrant); + void xinput_set_rumble(uint8_t left, uint8_t right); + uint8_t last_left_rumble; + uint8_t last_right_rumble; + XInputReport prev_report; +}; + +#endif // _XBOX360HOST_H_ diff --git a/src/addons/gamepad_usb_host_listener.cpp b/src/addons/gamepad_usb_host_listener.cpp index 811cf3f07c..6c6d6db4ed 100644 --- a/src/addons/gamepad_usb_host_listener.cpp +++ b/src/addons/gamepad_usb_host_listener.cpp @@ -9,9 +9,9 @@ #include "hosts/DrivingForceHost.h" #include "hosts/UltraStik360Host.h" #include "hosts/SwitchProHost.h" +#include "hosts/Xbox360Host.h" /* -#include "hosts/Xbox360Host.h" #include "hosts/XboxOneHost.h" */ @@ -38,7 +38,7 @@ void GamepadUSBHostListener::gamepadFeatureUpdate() { void GamepadUSBHostListener::mount(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) { // Only host one controller at a time - if ( _controller_host == nullptr ) return; + if ( _controller_host != nullptr ) return; uint16_t vid = 0, pid = 0; tuh_vid_pid_get(dev_addr, &vid, &pid); @@ -82,16 +82,17 @@ void GamepadUSBHostListener::mount(uint8_t dev_addr, uint8_t instance, uint8_t c void GamepadUSBHostListener::xmount(uint8_t dev_addr, uint8_t instance, uint8_t controllerType, uint8_t subtype) { // Only host one controller at a time - if ( _controller_host == nullptr ) return; + if ( _controller_host != nullptr ) return; uint16_t vid, pid; tuh_vid_pid_get(dev_addr, &vid, &pid); // Match is a static function, setup and init happens after we've established something _controller_host = nullptr; - /*if ( Xbox360Host::match(vid, pid) && controllerType == 1 ) { + if ( Xbox360Host::match(vid, pid) && controllerType == 1 ) { _controller_host = new Xbox360Host(); - } else if ( XboxOneHost::match(vid, pid && controllerType == 2 ) { + } + /* else if ( XboxOneHost::match(vid, pid && controllerType == 2 ) { _controller_host = new XboxOneHost(); }*/ diff --git a/src/hosts/Xbox360Host.cpp b/src/hosts/Xbox360Host.cpp index 7f67d3ab4e..1e00bc201c 100644 --- a/src/hosts/Xbox360Host.cpp +++ b/src/hosts/Xbox360Host.cpp @@ -1,76 +1,53 @@ #include "hosts/Xbox360Host.h" +#include "drivers/shared/xinput_host.h" +#include + +bool Xbox360Host::match(uint16_t vendor_id, uint16_t product_id) { + if ( vendor_id == 0x045E ) { + switch(product_id) { + case 0x028E: + return true; + } + } + return false; +} -void GamepadUSBHostListener::xbox360_set_led(uint8_t dev_addr, uint8_t instance, uint8_t quadrant) { - uint8_t out[32] = { 0 }; +void Xbox360Host::initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { + // Setup our vars + _dev_addr = dev_addr; + _instance = instance; + _vendor_id = vendor_id; + _product_id = product_id; - memcpy(out, XBOX360_WIRED_LED, sizeof(XBOX360_WIRED_LED)); - out[2] = (quadrant == 0) ? 0 : (quadrant + 5); - bool ret = tuh_xinput_send_report(dev_addr, instance, out, sizeof(XBOX360_WIRED_LED)); - if (ret) { - tuh_xinput_wait_for_tx(dev_addr, instance); - } -} + _controller_host_state.buttons = 0; + _controller_host_state.dpad = 0; + _controller_host_state.lx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ly = GAMEPAD_JOYSTICK_MID; + _controller_host_state.rx = GAMEPAD_JOYSTICK_MID; + _controller_host_state.ry = GAMEPAD_JOYSTICK_MID; -void GamepadUSBHostListener::xinput_set_rumble(uint8_t dev_addr, uint8_t instance, uint8_t left, uint8_t right) { - uint8_t out[32] = { 0 }; - uint16_t len = 0; - switch (_controller_type) { - case xinput_type_t::XBOX360: { - memcpy(out, XBOX360_WIRED_RUMBLE, sizeof(XBOX360_WIRED_RUMBLE)); - out[3] = left; - out[4] = right; - len = sizeof(XBOX360_WIRED_RUMBLE); - break; - } - case xinput_type_t::XBOXONE: { - memcpy(out, XBOXONE_RUMBLE, sizeof(XBOXONE_RUMBLE)); - out[8] = left >> 1; // 7-bit (0-127) - out[9] = right >> 1; // 7-bit (0-127) - len = sizeof(XBOXONE_RUMBLE); - break; - } - default: - return; - } - tuh_xinput_wait_for_tx(dev_addr, instance); - bool ret = tuh_xinput_send_report(dev_addr, instance, out, len); - if (ret) { - tuh_xinput_wait_for_tx(dev_addr, instance); - } -} + last_left_rumble = 0; + last_right_rumble = 0; -void GamepadUSBHostListener::setup_xinput(uint8_t dev_addr, uint8_t instance) { - Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + memset(&prev_report, 0, sizeof(XInputReport)); - switch (_controller_type) { - case xinput_type_t::XBOX360: { - uint32_t quadrants = gamepad->auxState.playerID.value; - if (quadrants == 0) - quadrants = 1; - for (uint32_t i = 0; i < quadrants; i++) { - xbox360_set_led(dev_addr, instance, i); - } - - xinput_set_rumble(dev_addr, instance, 0, 0); - break; - } - case xinput_type_t::XBOXONE: { - // implement xbox one init here... currently not supported - break; - } - default: // unsupported - break; + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + + uint32_t quadrants = gamepad->auxState.playerID.value; + if (quadrants == 0) + quadrants = 1; + for (uint32_t i = 0; i < quadrants; i++) { + xbox360_set_led(i); } - tuh_xinput_receive_report(dev_addr, instance); + + xinput_set_rumble(0, 0); + tuh_xinput_receive_report(_dev_addr, _instance); } -void GamepadUSBHostListener::update_xinput(uint8_t dev_addr, uint8_t instance) { +void Xbox360Host::update() { Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); - static uint8_t last_left_rumble = 0; - static uint8_t last_right_rumble = 0; - // rumble gamepad->auxState.haptics.leftActuator.enabled = 1; gamepad->auxState.haptics.rightActuator.enabled = 1; @@ -89,13 +66,13 @@ void GamepadUSBHostListener::update_xinput(uint8_t dev_addr, uint8_t instance) { last_left_rumble = leftRumble; last_right_rumble = rightRumble; - xinput_set_rumble(dev_addr, instance, leftRumble, rightRumble); + xinput_set_rumble(leftRumble, rightRumble); } -void GamepadUSBHostListener::process_xbox360(uint8_t const* report, uint16_t len) { +void Xbox360Host::process(uint8_t const* report, uint16_t len) { XInputReport controller_report; - static XInputReport prev_report = { 0 }; + if (len < sizeof(XInputReport)) { #if GAMEPAD_HOST_DEBUG @@ -135,7 +112,47 @@ void GamepadUSBHostListener::process_xbox360(uint8_t const* report, uint16_t len _controller_host_state.lt = controller_report.lt; _controller_host_state.rt = controller_report.rt; - _controller_host_analog = true; - prev_report = controller_report; + memcpy(&prev_report, &controller_report, sizeof(XInputReport)); +} + + +void Xbox360Host::gamepad(Gamepad * gamepad) { + // Override the Gamepad + gamepad->hasAnalogTriggers = true; + gamepad->hasLeftAnalogStick = true; + gamepad->hasRightAnalogStick = true; + gamepad->state.dpad |= _controller_host_state.dpad; + gamepad->state.buttons |= _controller_host_state.buttons; + gamepad->state.lx = _controller_host_state.lx; + gamepad->state.ly = _controller_host_state.ly; + gamepad->state.rx = _controller_host_state.rx; + gamepad->state.ry = _controller_host_state.ry; + gamepad->state.rt = _controller_host_state.rt; + gamepad->state.lt = _controller_host_state.lt; +} + +void Xbox360Host::xbox360_set_led(uint8_t quadrant) { + uint8_t out[32] = { 0 }; + + memcpy(out, XBOX360_WIRED_LED, sizeof(XBOX360_WIRED_LED)); + out[2] = (quadrant == 0) ? 0 : (quadrant + 5); + bool ret = tuh_xinput_send_report(_dev_addr, _instance, out, sizeof(XBOX360_WIRED_LED)); + if (ret) { + tuh_xinput_wait_for_tx(_dev_addr, _instance); + } +} + +void Xbox360Host::xinput_set_rumble(uint8_t left, uint8_t right) { + uint8_t out[32]; + memset(out, 0, 32); + uint16_t len = sizeof(XBOX360_WIRED_RUMBLE); + memcpy(out, XBOX360_WIRED_RUMBLE, sizeof(XBOX360_WIRED_RUMBLE)); + out[3] = left; + out[4] = right; + tuh_xinput_wait_for_tx(_dev_addr, _instance); + bool ret = tuh_xinput_send_report(_dev_addr, _instance, out, len); + if (ret) { + tuh_xinput_wait_for_tx(_dev_addr, _instance); + } } From f8041ebba9c91804a32bcf5cd40bb26a28469439 Mon Sep 17 00:00:00 2001 From: Luke A Date: Sun, 21 Jun 2026 13:58:38 -0400 Subject: [PATCH 04/11] Clean-up headers and need to test this --- headers/addons/gamepad_usb_host_listener.h | 55 ---------------------- src/addons/gamepad_usb_host_listener.cpp | 2 - 2 files changed, 57 deletions(-) diff --git a/headers/addons/gamepad_usb_host_listener.h b/headers/addons/gamepad_usb_host_listener.h index 9eb490b952..c1209a1835 100644 --- a/headers/addons/gamepad_usb_host_listener.h +++ b/headers/addons/gamepad_usb_host_listener.h @@ -4,17 +4,12 @@ #include "usblistener.h" #include "gamepad.h" #include "host/usbh.h" -#include "drivers/ps4/PS4Descriptors.h" -#include "drivers/switchpro/SwitchProDescriptors.h" #include "gphost.h" // Move to individuals #define GAMEPAD_HOST_DEBUG false -// Move to DS4 and Switch Pro -#define GAMEPAD_HOST_USE_FEATURES true - const uint32_t GAMEPAD_HOST_POLL_INTERVAL_MS = 3; // Add other controller structs here @@ -34,56 +29,6 @@ class GamepadUSBHostListener : public USBListener { uint8_t _controller_instance; GPHost * _controller_host; uint32_t _next_update; - - /* - //GamepadState _controller_host_state; - //bool _controller_host_analog = true; - - //uint8_t _controller_type = 0; // 0 = HID, 1 = Xbox 360, 2 = Xbox One (see xinput_type_t) - uint32_t _next_update = 0; - // reliable update (e.g for rumble) called every GAMEPAD_HOST_POLL_INTERVAL_MS ms - //void update_ctrlr(); - //void process_ctrlr_report(uint8_t dev_addr, uint8_t const* report, uint16_t len); - - void process_ds(uint8_t const* report, uint16_t len); - - // switch pro - bool switchProFinished = false; - uint8_t switchProState = SwitchOutputSubtypes::IDENTIFY; - uint8_t switchReportCounter = 0; - uint8_t lastSwitchLed = 0; - void setup_switch_pro(uint8_t const *report, uint16_t len); - void update_switch_pro(); - void process_switch_pro(uint8_t const* report, uint16_t len); - uint8_t get_next_switch_counter(); - - void process_stadia(uint8_t const* report, uint16_t len); - - void process_ultrastik360(uint8_t const* report, uint16_t len); - - void xbox360_set_led(uint8_t dev_addr, uint8_t instance, uint8_t quadrant); - // universal xinput rumble packet - void xinput_set_rumble(uint8_t dev_addr, uint8_t instance, uint8_t left, uint8_t right); - // universal xinput setup (xbox one init not included) - void setup_xinput(uint8_t dev_addr, uint8_t instance); - // sync controller rumble and LED states - void update_xinput(uint8_t dev_addr, uint8_t instance); - void process_xbox360(uint8_t const* report, uint16_t len); - - uint16_t controller_pid, controller_vid; - - uint32_t map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max); - - // check if different than 2 - bool diff_than_2(uint8_t x, uint8_t y); - // check if 2 reports are different enough - bool diff_report(PS4Report const* rpt1, PS4Report const* rpt2); - - // wheel check - bool isDFInit = false; - void setup_df_wheel(); - void process_dfgt(uint8_t const* report, uint16_t len); - */ }; #endif diff --git a/src/addons/gamepad_usb_host_listener.cpp b/src/addons/gamepad_usb_host_listener.cpp index 6c6d6db4ed..cf3c26db15 100644 --- a/src/addons/gamepad_usb_host_listener.cpp +++ b/src/addons/gamepad_usb_host_listener.cpp @@ -43,8 +43,6 @@ void GamepadUSBHostListener::mount(uint8_t dev_addr, uint8_t instance, uint8_t c uint16_t vid = 0, pid = 0; tuh_vid_pid_get(dev_addr, &vid, &pid); - // TODO: Make a list of static functions + pointers - // Match is a static function, setup and init happens after we've established something _controller_host = nullptr; if ( DualshockPS4Host::match(vid, pid) ) { _controller_host = (GPHost*)(new DualshockPS4Host()); From b9d44c73b87b93251168d05f4362faeb5e01302a Mon Sep 17 00:00:00 2001 From: Luke A Date: Tue, 23 Jun 2026 21:11:15 -0400 Subject: [PATCH 05/11] Updating to latest --- headers/addons/gamepad_usb_host_listener.h | 2 +- src/addons/gamepad_usb_host.cpp | 2 +- src/addons/gamepad_usb_host_listener.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/headers/addons/gamepad_usb_host_listener.h b/headers/addons/gamepad_usb_host_listener.h index c1209a1835..266ca1ae4d 100644 --- a/headers/addons/gamepad_usb_host_listener.h +++ b/headers/addons/gamepad_usb_host_listener.h @@ -23,7 +23,7 @@ class GamepadUSBHostListener : public USBListener { virtual void report_sent(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) {} virtual void set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len); virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len); - void gamepadFeatureUpdate(); + void update(); private: uint8_t _controller_dev_addr; uint8_t _controller_instance; diff --git a/src/addons/gamepad_usb_host.cpp b/src/addons/gamepad_usb_host.cpp index d6f9bf290f..2f29815a6e 100644 --- a/src/addons/gamepad_usb_host.cpp +++ b/src/addons/gamepad_usb_host.cpp @@ -17,5 +17,5 @@ void GamepadUSBHostAddon::setup() { } void GamepadUSBHostAddon::preprocess() { - ((GamepadUSBHostListener*)listener)->gamepadFeatureUpdate(); + ((GamepadUSBHostListener*)listener)->update(); } \ No newline at end of file diff --git a/src/addons/gamepad_usb_host_listener.cpp b/src/addons/gamepad_usb_host_listener.cpp index cf3c26db15..a3eb3b1f80 100644 --- a/src/addons/gamepad_usb_host_listener.cpp +++ b/src/addons/gamepad_usb_host_listener.cpp @@ -22,7 +22,7 @@ void GamepadUSBHostListener::setup() { _next_update = 0; } -void GamepadUSBHostListener::gamepadFeatureUpdate() { +void GamepadUSBHostListener::update() { if ( _controller_host == nullptr ) return; // Controller host updates (rumble, LEDs, etc. outside of gamepad input reports) @@ -45,7 +45,7 @@ void GamepadUSBHostListener::mount(uint8_t dev_addr, uint8_t instance, uint8_t c _controller_host = nullptr; if ( DualshockPS4Host::match(vid, pid) ) { - _controller_host = (GPHost*)(new DualshockPS4Host()); + _controller_host = new DualshockPS4Host(); }else if ( DualsensePS5Host::match(vid, pid) ) { _controller_host = new DualsensePS5Host(); } else if ( GoogleStadiaHost::match(vid, pid) ) { From e2fcaf398acc90f8df829b512d4513129f904feb Mon Sep 17 00:00:00 2001 From: Luke A Date: Sat, 27 Jun 2026 22:01:07 -0400 Subject: [PATCH 06/11] Expanded match and fixed a bug with gphost.h --- headers/gphost.h | 2 +- headers/hosts/DrivingForceHost.h | 2 +- headers/hosts/DualsensePS5Host.h | 2 +- headers/hosts/DualshockPS4Host.h | 2 +- headers/hosts/GoogleStadiaHost.h | 2 +- headers/hosts/SwitchProHost.h | 2 +- headers/hosts/Ultrastik360Host.h | 2 +- headers/hosts/Xbox360Host.h | 2 +- src/addons/gamepad_usb_host_listener.cpp | 14 +++++----- src/hosts/DrivingForceHost.cpp | 2 +- src/hosts/DualsensePS5Host.cpp | 2 +- src/hosts/DualshockPS4Host.cpp | 35 +++++++++++++++++++++++- src/hosts/GoogleStadioHost.cpp | 2 +- src/hosts/SwitchProHost.cpp | 2 +- src/hosts/Ultrastik360Host.cpp | 2 +- src/hosts/Xbox360Host.cpp | 13 +++++---- 16 files changed, 61 insertions(+), 27 deletions(-) diff --git a/headers/gphost.h b/headers/gphost.h index be5df642b1..a54e1614f8 100644 --- a/headers/gphost.h +++ b/headers/gphost.h @@ -9,7 +9,7 @@ #include #include "gamepad/GamepadState.h" -#include "Gamepad.h" +#include "gamepad.h" // // GP2040-CE USB Device Class Driver diff --git a/headers/hosts/DrivingForceHost.h b/headers/hosts/DrivingForceHost.h index d3206e3b27..b7339b58bb 100644 --- a/headers/hosts/DrivingForceHost.h +++ b/headers/hosts/DrivingForceHost.h @@ -15,7 +15,7 @@ // class DrivingForceHost : public GPHost { public: - static bool match(uint16_t vendor_id, uint16_t product_id); + static bool match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void gamepad(Gamepad * gamepad); virtual void process(uint8_t const* report, uint16_t len); diff --git a/headers/hosts/DualsensePS5Host.h b/headers/hosts/DualsensePS5Host.h index c8208c68ef..a57fafaaad 100644 --- a/headers/hosts/DualsensePS5Host.h +++ b/headers/hosts/DualsensePS5Host.h @@ -15,7 +15,7 @@ // class DualsensePS5Host : public GPHost { public: - static bool match(uint16_t vendor_id, uint16_t product_id); + static bool match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void gamepad(Gamepad * gamepad); virtual void process(uint8_t const* report, uint16_t len); diff --git a/headers/hosts/DualshockPS4Host.h b/headers/hosts/DualshockPS4Host.h index d1f601b9d7..7b4146d7e5 100644 --- a/headers/hosts/DualshockPS4Host.h +++ b/headers/hosts/DualshockPS4Host.h @@ -15,8 +15,8 @@ // GP2040-CE USB Host Driver // class DualshockPS4Host : public GPHost { + static bool match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); public: - static bool match(uint16_t vendor_id, uint16_t product_id); virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void gamepad(Gamepad * gamepad); virtual void process(uint8_t const* report, uint16_t len); diff --git a/headers/hosts/GoogleStadiaHost.h b/headers/hosts/GoogleStadiaHost.h index 645c7e1e66..ea4283f9e4 100644 --- a/headers/hosts/GoogleStadiaHost.h +++ b/headers/hosts/GoogleStadiaHost.h @@ -50,7 +50,7 @@ typedef struct TU_ATTR_PACKED // class GoogleStadiaHost : public GPHost { public: - static bool match(uint16_t vendor_id, uint16_t product_id); + static bool match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void gamepad(Gamepad * gamepad); virtual void process(uint8_t const* report, uint16_t len); diff --git a/headers/hosts/SwitchProHost.h b/headers/hosts/SwitchProHost.h index 392256bbd8..e74400265f 100644 --- a/headers/hosts/SwitchProHost.h +++ b/headers/hosts/SwitchProHost.h @@ -25,7 +25,7 @@ typedef struct __attribute__((packed)) { // class SwitchProHost : public GPHost { public: - static bool match(uint16_t vendor_id, uint16_t product_id); + static bool match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void gamepad(Gamepad * gamepad); virtual void process(uint8_t const* report, uint16_t len); diff --git a/headers/hosts/Ultrastik360Host.h b/headers/hosts/Ultrastik360Host.h index 37c83d2329..6fe1d8a699 100644 --- a/headers/hosts/Ultrastik360Host.h +++ b/headers/hosts/Ultrastik360Host.h @@ -40,7 +40,7 @@ typedef struct TU_ATTR_PACKED // class Ultrastik360Host : public GPHost { public: - static bool match(uint16_t vendor_id, uint16_t product_id); + static bool match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void gamepad(Gamepad * gamepad); virtual void process(uint8_t const* report, uint16_t len); diff --git a/headers/hosts/Xbox360Host.h b/headers/hosts/Xbox360Host.h index b815d19301..ea69771cf1 100644 --- a/headers/hosts/Xbox360Host.h +++ b/headers/hosts/Xbox360Host.h @@ -15,7 +15,7 @@ // class Xbox360Host : public GPHost { public: - static bool match(uint16_t vendor_id, uint16_t product_id); + static bool match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t controller_type); virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void gamepad(Gamepad * gamepad); virtual void process(uint8_t const* report, uint16_t len); diff --git a/src/addons/gamepad_usb_host_listener.cpp b/src/addons/gamepad_usb_host_listener.cpp index a3eb3b1f80..76cc8d8ba2 100644 --- a/src/addons/gamepad_usb_host_listener.cpp +++ b/src/addons/gamepad_usb_host_listener.cpp @@ -44,17 +44,17 @@ void GamepadUSBHostListener::mount(uint8_t dev_addr, uint8_t instance, uint8_t c tuh_vid_pid_get(dev_addr, &vid, &pid); _controller_host = nullptr; - if ( DualshockPS4Host::match(vid, pid) ) { + if ( DualshockPS4Host::match(dev_addr, instance, vid, pid, desc_report, desc_len) ) { _controller_host = new DualshockPS4Host(); - }else if ( DualsensePS5Host::match(vid, pid) ) { + }else if ( DualsensePS5Host::match(dev_addr, instance, vid, pid, desc_report, desc_len) ) { _controller_host = new DualsensePS5Host(); - } else if ( GoogleStadiaHost::match(vid, pid) ) { + } else if ( GoogleStadiaHost::match(dev_addr, instance, vid, pid, desc_report, desc_len) ) { _controller_host = new GoogleStadiaHost(); - } else if ( DrivingForceHost::match(vid, pid) ) { + } else if ( DrivingForceHost::match(dev_addr, instance, vid, pid, desc_report, desc_len) ) { _controller_host = new DrivingForceHost(); - } else if ( Ultrastik360Host::match(vid, pid) ) { + } else if ( Ultrastik360Host::match(dev_addr, instance, vid, pid, desc_report, desc_len) ) { _controller_host = new Ultrastik360Host(); - } else if ( SwitchProHost::match(vid, pid) ) { + } else if ( SwitchProHost::match(dev_addr, instance, vid, pid, desc_report, desc_len) ) { _controller_host = new SwitchProHost(); } else { // Better way to determine if its a generic HID? @@ -87,7 +87,7 @@ void GamepadUSBHostListener::xmount(uint8_t dev_addr, uint8_t instance, uint8_t // Match is a static function, setup and init happens after we've established something _controller_host = nullptr; - if ( Xbox360Host::match(vid, pid) && controllerType == 1 ) { + if ( Xbox360Host::match(dev_addr, instance, vid, pid, controllerType) ) { _controller_host = new Xbox360Host(); } /* else if ( XboxOneHost::match(vid, pid && controllerType == 2 ) { diff --git a/src/hosts/DrivingForceHost.cpp b/src/hosts/DrivingForceHost.cpp index 5fb1f7bb00..aed9534d38 100644 --- a/src/hosts/DrivingForceHost.cpp +++ b/src/hosts/DrivingForceHost.cpp @@ -5,7 +5,7 @@ #include "class/hid/hid.h" #include "device/usbd_pvt.h" -bool DrivingForceHost::match(uint16_t vendor_id, uint16_t product_id) { +bool DrivingForceHost::match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { if ( vendor_id == 0x46d) { switch(product_id) { case 0xC294: // compatible mode diff --git a/src/hosts/DualsensePS5Host.cpp b/src/hosts/DualsensePS5Host.cpp index d72d6ed9ea..4c3678b249 100644 --- a/src/hosts/DualsensePS5Host.cpp +++ b/src/hosts/DualsensePS5Host.cpp @@ -1,6 +1,6 @@ #include "hosts/DualsensePS5Host.h" -bool DualsensePS5Host::match(uint16_t vendor_id, uint16_t product_id) { +bool DualsensePS5Host::match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { // Playstation 5 Dualsense if ( vendor_id == 0x054c) { switch(product_id) { diff --git a/src/hosts/DualshockPS4Host.cpp b/src/hosts/DualshockPS4Host.cpp index 838cfed985..dbd5d690c5 100644 --- a/src/hosts/DualshockPS4Host.cpp +++ b/src/hosts/DualshockPS4Host.cpp @@ -3,10 +3,16 @@ #include "storagemanager.h" // Static match function -bool DualshockPS4Host::match(uint16_t vendor_id, uint16_t product_id) { +bool DualshockPS4Host::match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { + // Victrix Pro FS + + // Nacon Revolution Pro 3 + // Hori (PS4) if ( vendor_id == 0x0F0D ) { switch(product_id) { + // Horipad FPS Plus? + // Hori Fighting Commander case 0x00EE: return true; } @@ -46,6 +52,33 @@ bool DualshockPS4Host::match(uint16_t vendor_id, uint16_t product_id) { } } + // Check for any other PS4 controller + // Find the report for PS4 definition if this controller has one + tuh_hid_report_info_t * report_info = nullptr; + uint8_t reportIDCount = 0; + uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 0, desc_report, desc_len); + report_info = new tuh_hid_report_info_t[report_count]; + for(uint8_t i = 0; i < report_count; i++) { +#if GAMEPAD_HOST_DEBUG + //printf("Report: %02x, Usage: %04x, Usage Page: %04x\n", report_info[i].report_id, report_info[i].usage_page, report_info[i].usage); +#endif + if (report_info[i].report_id == PS4AuthReport::PS4_DEFINITION || + report_info[i].report_id == PS4AuthReport::PS4_SET_FEATURE_STATE || + report_info[i].report_id == PS4AuthReport::PS4_SET_AUTH_PAYLOAD || + report_info[i].report_id == PS4AuthReport::PS4_GET_SIGNATURE_NONCE || + report_info[i].report_id == PS4AuthReport::PS4_GET_SIGNING_STATE || + report_info[i].report_id == PS4AuthReport::PS4_RESET_AUTH ) { + reportIDCount++; + } + } + + delete report_info; + + // Auto-detected a PS4 + if ( reportIDCount == 6 ) { + return true; + } + return false; } diff --git a/src/hosts/GoogleStadioHost.cpp b/src/hosts/GoogleStadioHost.cpp index 5bdcc1644c..0592b3c007 100644 --- a/src/hosts/GoogleStadioHost.cpp +++ b/src/hosts/GoogleStadioHost.cpp @@ -1,7 +1,7 @@ #include "hosts/GoogleStadiaHost.h" // Static match function -bool GoogleStadiaHost::match(uint16_t vendor_id, uint16_t product_id) { +bool GoogleStadiaHost::match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { // Google Stadia if ( vendor_id == 0x18D1 ) { switch(product_id) { diff --git a/src/hosts/SwitchProHost.cpp b/src/hosts/SwitchProHost.cpp index 0b109c8036..14701886a5 100644 --- a/src/hosts/SwitchProHost.cpp +++ b/src/hosts/SwitchProHost.cpp @@ -6,7 +6,7 @@ #include "storagemanager.h" // Match -bool SwitchProHost::match(uint16_t vendor_id, uint16_t product_id) { +bool SwitchProHost::match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { // Nintendo if ( vendor_id == 0x057E) { switch(product_id) { diff --git a/src/hosts/Ultrastik360Host.cpp b/src/hosts/Ultrastik360Host.cpp index 581cce2633..0310812162 100644 --- a/src/hosts/Ultrastik360Host.cpp +++ b/src/hosts/Ultrastik360Host.cpp @@ -1,6 +1,6 @@ #include "hosts/Ultrastik360Host.h" -bool Ultrastik360Host::match(uint16_t vendor_id, uint16_t product_id) { +bool Ultrastik360Host::match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { // Ultrastik 360 // if ( vendor_id == 0x054c) { // ask community what this is switch(product_id) { diff --git a/src/hosts/Xbox360Host.cpp b/src/hosts/Xbox360Host.cpp index 1e00bc201c..506583bd13 100644 --- a/src/hosts/Xbox360Host.cpp +++ b/src/hosts/Xbox360Host.cpp @@ -2,14 +2,15 @@ #include "drivers/shared/xinput_host.h" #include -bool Xbox360Host::match(uint16_t vendor_id, uint16_t product_id) { - if ( vendor_id == 0x045E ) { - switch(product_id) { - case 0x028E: - return true; +bool Xbox360Host::match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t controller_type) { + if ( controller_type == 1 ) { + if ( vendor_id == 0x045E ) { + switch(product_id) { + case 0x028E: + return true; + } } } - return false; } From 64106ce1b455c9f01fb48fbf54d641d86881692c Mon Sep 17 00:00:00 2001 From: Luke A Date: Sat, 27 Jun 2026 22:06:52 -0400 Subject: [PATCH 07/11] Fixed header define --- headers/hosts/Ultrastik360Host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headers/hosts/Ultrastik360Host.h b/headers/hosts/Ultrastik360Host.h index 6fe1d8a699..a7bca2ef67 100644 --- a/headers/hosts/Ultrastik360Host.h +++ b/headers/hosts/Ultrastik360Host.h @@ -4,7 +4,7 @@ */ #ifndef _ULTRASTIK360HOST_H_ -#define _ULTRASTIK360PROHOST_H_ +#define _ULTRASTIK360HOST_H_ #include From f89a7d0553aca49952012ed159dfbff9ecef85d7 Mon Sep 17 00:00:00 2001 From: Luke A Date: Sat, 27 Jun 2026 22:13:59 -0400 Subject: [PATCH 08/11] Another .h capital fix --- src/addons/gamepad_usb_host_listener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/addons/gamepad_usb_host_listener.cpp b/src/addons/gamepad_usb_host_listener.cpp index 76cc8d8ba2..cf330633e5 100644 --- a/src/addons/gamepad_usb_host_listener.cpp +++ b/src/addons/gamepad_usb_host_listener.cpp @@ -7,7 +7,7 @@ #include "hosts/DualshockPS4Host.h" #include "hosts/GoogleStadiaHost.h" #include "hosts/DrivingForceHost.h" -#include "hosts/UltraStik360Host.h" +#include "hosts/Ultrastik360Host.h" #include "hosts/SwitchProHost.h" #include "hosts/Xbox360Host.h" From aa03ed0b26fa6784e1ff03ad7f76a07942b470cf Mon Sep 17 00:00:00 2001 From: Luke A Date: Sat, 27 Jun 2026 22:19:52 -0400 Subject: [PATCH 09/11] Move match over to public: --- headers/hosts/DualshockPS4Host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headers/hosts/DualshockPS4Host.h b/headers/hosts/DualshockPS4Host.h index 7b4146d7e5..20342ce8f1 100644 --- a/headers/hosts/DualshockPS4Host.h +++ b/headers/hosts/DualshockPS4Host.h @@ -15,8 +15,8 @@ // GP2040-CE USB Host Driver // class DualshockPS4Host : public GPHost { - static bool match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); public: + static bool match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void initialize(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len); virtual void gamepad(Gamepad * gamepad); virtual void process(uint8_t const* report, uint16_t len); From f8ee971b775e5d9663071cfd732438086d8c65fc Mon Sep 17 00:00:00 2001 From: Luke A Date: Sat, 27 Jun 2026 22:29:06 -0400 Subject: [PATCH 10/11] Get rid of the autodetect and some of the debug code for the first PR --- headers/addons/gamepad_usb_host_listener.h | 2 +- src/hosts/DrivingForceHost.cpp | 2 +- src/hosts/DualshockPS4Host.cpp | 31 ++-------------------- src/hosts/Xbox360Host.cpp | 2 +- 4 files changed, 5 insertions(+), 32 deletions(-) diff --git a/headers/addons/gamepad_usb_host_listener.h b/headers/addons/gamepad_usb_host_listener.h index 266ca1ae4d..c3eadba2dc 100644 --- a/headers/addons/gamepad_usb_host_listener.h +++ b/headers/addons/gamepad_usb_host_listener.h @@ -8,7 +8,7 @@ #include "gphost.h" // Move to individuals -#define GAMEPAD_HOST_DEBUG false +//#define GAMEPAD_HOST_DEBUG 1 const uint32_t GAMEPAD_HOST_POLL_INTERVAL_MS = 3; diff --git a/src/hosts/DrivingForceHost.cpp b/src/hosts/DrivingForceHost.cpp index aed9534d38..4e7da10762 100644 --- a/src/hosts/DrivingForceHost.cpp +++ b/src/hosts/DrivingForceHost.cpp @@ -41,7 +41,7 @@ void DrivingForceHost::initialize(uint8_t dev_addr, uint8_t instance, uint16_t v void DrivingForceHost::process(uint8_t const* report, uint16_t len) { PS3ReportAlt ps3Report; memcpy(&ps3Report, report, len); -#if GAMEPAD_HOST_DEBUG +#ifdef GAMEPAD_HOST_DEBUG //printf("\033[2;0H"); //for (uint8_t i = 0; i < len; i++) { // printf("%02x ", report[i]); diff --git a/src/hosts/DualshockPS4Host.cpp b/src/hosts/DualshockPS4Host.cpp index dbd5d690c5..efe8c885d8 100644 --- a/src/hosts/DualshockPS4Host.cpp +++ b/src/hosts/DualshockPS4Host.cpp @@ -52,33 +52,6 @@ bool DualshockPS4Host::match(uint8_t dev_addr, uint8_t instance, uint16_t vendor } } - // Check for any other PS4 controller - // Find the report for PS4 definition if this controller has one - tuh_hid_report_info_t * report_info = nullptr; - uint8_t reportIDCount = 0; - uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 0, desc_report, desc_len); - report_info = new tuh_hid_report_info_t[report_count]; - for(uint8_t i = 0; i < report_count; i++) { -#if GAMEPAD_HOST_DEBUG - //printf("Report: %02x, Usage: %04x, Usage Page: %04x\n", report_info[i].report_id, report_info[i].usage_page, report_info[i].usage); -#endif - if (report_info[i].report_id == PS4AuthReport::PS4_DEFINITION || - report_info[i].report_id == PS4AuthReport::PS4_SET_FEATURE_STATE || - report_info[i].report_id == PS4AuthReport::PS4_SET_AUTH_PAYLOAD || - report_info[i].report_id == PS4AuthReport::PS4_GET_SIGNATURE_NONCE || - report_info[i].report_id == PS4AuthReport::PS4_GET_SIGNING_STATE || - report_info[i].report_id == PS4AuthReport::PS4_RESET_AUTH ) { - reportIDCount++; - } - } - - delete report_info; - - // Auto-detected a PS4 - if ( reportIDCount == 6 ) { - return true; - } - return false; } @@ -105,7 +78,7 @@ void DualshockPS4Host::initialize(uint8_t dev_addr, uint8_t instance, uint16_t v tuh_hid_report_info_t report_info[4]; uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 4, desc_report, desc_len); for(uint8_t i = 0; i < report_count; i++) { -#if GAMEPAD_HOST_DEBUG +#ifdef GAMEPAD_HOST_DEBUG //printf("Report: %02x, Usage: %04x, Usage Page: %04x\n", report_info[i].report_id, report_info[i].usage_page, report_info[i].usage); #endif if (report_info[i].report_id == PS4AuthReport::PS4_DEFINITION) { @@ -214,7 +187,7 @@ void DualshockPS4Host::get_report_complete(uint8_t dev_addr, uint8_t instance, u memcpy(&ds4Config, report_buffer+1, sizeof(PS4ControllerConfig)); if ((ds4Config.hidUsage == 0x2721) || (ds4Config.hidUsage == 0x2127)) { validPS4Definition = true; - #if GAMEPAD_HOST_DEBUG + #ifdef GAMEPAD_HOST_DEBUG //printf("PS4 controller details\n"); //printf("----------------------\n"); //printf("enableController: %d\n", ds4Config.features.enableController); diff --git a/src/hosts/Xbox360Host.cpp b/src/hosts/Xbox360Host.cpp index 506583bd13..cf59955eba 100644 --- a/src/hosts/Xbox360Host.cpp +++ b/src/hosts/Xbox360Host.cpp @@ -76,7 +76,7 @@ void Xbox360Host::process(uint8_t const* report, uint16_t len) { if (len < sizeof(XInputReport)) { -#if GAMEPAD_HOST_DEBUG +#ifdef GAMEPAD_HOST_DEBUG printf("Xbox 360 report too small: %d bytes\n", len); #endif return; From d9388e4827fa7cbcd2f42e61dbe512c87b98602d Mon Sep 17 00:00:00 2001 From: Luke A Date: Sun, 28 Jun 2026 14:36:04 -0400 Subject: [PATCH 11/11] Fix up dualshock4, fixed a few aligned structs, added a few more matching controllers --- headers/addons/gamepad_usb_host_listener.h | 3 - .../drivers/p5general/P5GeneralDescriptors.h | 3 +- headers/drivers/ps4/PS4Descriptors.h | 50 ++++----- headers/hosts/DualshockPS4Host.h | 8 +- src/drivers/switchpro/SwitchProDriver.cpp | 2 - src/hosts/DualshockPS4Host.cpp | 102 ++++++++---------- src/hosts/SwitchProHost.cpp | 6 +- 7 files changed, 80 insertions(+), 94 deletions(-) diff --git a/headers/addons/gamepad_usb_host_listener.h b/headers/addons/gamepad_usb_host_listener.h index c3eadba2dc..492ace0cfa 100644 --- a/headers/addons/gamepad_usb_host_listener.h +++ b/headers/addons/gamepad_usb_host_listener.h @@ -7,9 +7,6 @@ #include "gphost.h" -// Move to individuals -//#define GAMEPAD_HOST_DEBUG 1 - const uint32_t GAMEPAD_HOST_POLL_INTERVAL_MS = 3; // Add other controller structs here diff --git a/headers/drivers/p5general/P5GeneralDescriptors.h b/headers/drivers/p5general/P5GeneralDescriptors.h index 99b20c5209..3bf6e32e56 100644 --- a/headers/drivers/p5general/P5GeneralDescriptors.h +++ b/headers/drivers/p5general/P5GeneralDescriptors.h @@ -59,8 +59,7 @@ #define P5GENERAL_GYRO_RES 1024 #define P5GENERAL_GYRO_RANGE (P5GENERAL_GYRO_RES * 2048) - -typedef struct __attribute__((packed)) { +typedef struct __attribute__((packed, aligned(1))) { uint8_t report_id; uint8_t left_stick_x; uint8_t left_stick_y; diff --git a/headers/drivers/ps4/PS4Descriptors.h b/headers/drivers/ps4/PS4Descriptors.h index 9150ef3d41..fecbeae882 100644 --- a/headers/drivers/ps4/PS4Descriptors.h +++ b/headers/drivers/ps4/PS4Descriptors.h @@ -104,7 +104,7 @@ #define PS4_GYRO_RES 1024 #define PS4_GYRO_RANGE (PS4_GYRO_RES * 2048) -struct TouchpadXY { +typedef struct __attribute__((packed, aligned(1))){ uint8_t counter : 7; uint8_t unpressed : 1; @@ -121,20 +121,20 @@ struct TouchpadXY { data[2] = y >> 4; return; } -}; +} TouchpadXY ; -struct TouchpadData { +typedef struct __attribute__((packed, aligned(1))) { TouchpadXY p1; TouchpadXY p2; -}; +} TouchpadData; -struct PSSensor { +typedef struct __attribute__((packed, aligned(1))) { int16_t x; int16_t y; int16_t z; -}; +} PSSensor; -struct PSSensorData { +typedef struct __attribute__((packed, aligned(1))){ uint16_t battery; PSSensor gyroscope; PSSensor accelerometer; @@ -149,9 +149,9 @@ struct PSSensorData { uint8_t notConnected : 1; uint8_t extData3 : 5; uint8_t misc2; -} __attribute__((packed)); +} PSSensorData; -typedef struct __attribute__((packed)) { +typedef struct __attribute__((packed, aligned(1))) { // 0 uint8_t reportID; @@ -159,23 +159,23 @@ typedef struct __attribute__((packed)) { uint8_t enableUpdateRumble : 1; uint8_t enableUpdateLED : 1; uint8_t enableUpdateLEDBlink : 1; - uint8_t enableUpdateExtData : 1; + uint8_t enableUpdateExtWrite : 1; uint8_t enableUpdateVolLeft : 1; uint8_t enableUpdateVolRight : 1; uint8_t enableUpdateVolMic : 1; uint8_t enableUpdateVolSpeaker : 1; // 2 - uint8_t : 8; + uint8_t empty; // 3 uint8_t unknown0; // 4 - uint8_t rumbleRight; + uint8_t rumbleRight; // weak // 5 - uint8_t rumbleLeft; + uint8_t rumbleLeft; // strong // 6 uint8_t ledRed; @@ -193,7 +193,7 @@ typedef struct __attribute__((packed)) { uint8_t ledBlinkOff; // 11 - uint8_t extData[8]; + uint8_t extData[8]; // sent to I2C EXT port, stored in 8x8 byte block // 19 uint8_t volumeLeft; // 0x00-0x4F @@ -214,7 +214,7 @@ typedef struct __attribute__((packed)) { uint8_t padding[8]; } PS4FeatureOutputReport; -typedef struct __attribute__((packed)) { +typedef struct __attribute__((packed, aligned(1))) { uint16_t gyroRange = 0; uint16_t gyroResPerDegDenom = 0; uint16_t gyroResPerDegNumer = 0; @@ -222,7 +222,7 @@ typedef struct __attribute__((packed)) { uint16_t accelResPerG = 0; } PS4IMUConfig; -typedef struct __attribute__((packed)) { +typedef struct __attribute__((packed, aligned(1))) { uint16_t hidUsage = 0x2127; uint8_t mystery0 = 0x04; @@ -252,7 +252,7 @@ typedef struct __attribute__((packed)) { uint8_t mystery2[21] = {0x00}; } PS4ControllerConfig; -typedef struct __attribute__((packed)) { +typedef struct __attribute__((packed, aligned(1))) { uint8_t reportID; uint8_t leftStickX; uint8_t leftStickY; @@ -289,7 +289,7 @@ typedef struct __attribute__((packed)) { union { uint8_t miscData[54]; - struct __attribute__((packed)) { + struct __attribute__((packed, aligned(1))) { // 16 bit timing counter uint16_t axisTiming; @@ -302,7 +302,7 @@ typedef struct __attribute__((packed)) { uint8_t mystery2[21]; } gamepad; - struct __attribute__((packed)) { + struct __attribute__((packed, aligned(1))) { uint8_t mystery0[22]; uint8_t powerLevel : 4; @@ -317,7 +317,7 @@ typedef struct __attribute__((packed)) { union { uint8_t fretValue; - struct __attribute__((packed)) { + struct __attribute__((packed, aligned(1))) { uint8_t green : 1; uint8_t red : 1; uint8_t yellow : 1; @@ -330,7 +330,7 @@ typedef struct __attribute__((packed)) { union { uint8_t soloFretValue; - struct __attribute__((packed)) { + struct __attribute__((packed, aligned(1))) { uint8_t green : 1; uint8_t red : 1; uint8_t yellow : 1; @@ -342,7 +342,7 @@ typedef struct __attribute__((packed)) { uint8_t mystery2[14]; } guitar; - struct __attribute__((packed)) { + struct __attribute__((packed, aligned(1))) { uint8_t mystery0[22]; uint8_t powerLevel : 4; @@ -361,7 +361,7 @@ typedef struct __attribute__((packed)) { uint8_t mystery2[12]; } drums; - struct __attribute__((packed)) { + struct __attribute__((packed, aligned(1))) { uint8_t mystery0[22]; uint8_t powerLevel : 4; @@ -379,7 +379,7 @@ typedef struct __attribute__((packed)) { uint8_t pedalLeft; uint8_t pedalRight; } hotas; - struct __attribute__((packed)) { + struct __attribute__((packed, aligned(1))) { uint8_t mystery0[22]; uint8_t powerLevel : 4; @@ -395,7 +395,7 @@ typedef struct __attribute__((packed)) { union { uint8_t shifterValue; - struct __attribute__((packed)) { + struct __attribute__((packed, aligned(1))) { uint8_t shifterGear1 : 1; uint8_t shifterGear2 : 1; uint8_t shifterGear3 : 1; diff --git a/headers/hosts/DualshockPS4Host.h b/headers/hosts/DualshockPS4Host.h index 20342ce8f1..1c7ee560c8 100644 --- a/headers/hosts/DualshockPS4Host.h +++ b/headers/hosts/DualshockPS4Host.h @@ -23,12 +23,14 @@ class DualshockPS4Host : public GPHost { virtual void update(); virtual void shutdown() {} virtual void set_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} - virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len); + virtual void get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) {} private: // Controller report processor functions PS4Report prevReport; - bool validPS4Definition; - PS4ControllerConfig ds4Config; + PS4FeatureOutputReport last_controller_output; + PS4FeatureOutputReport controller_output; + uint8_t last_rumble_left; + uint8_t last_rumble_right; uint8_t report_buffer[PS4_ENDPOINT_SIZE]; uint32_t map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max); bool diff_than_2(uint8_t x, uint8_t y); diff --git a/src/drivers/switchpro/SwitchProDriver.cpp b/src/drivers/switchpro/SwitchProDriver.cpp index 1a02229e9f..41e97b80c0 100644 --- a/src/drivers/switchpro/SwitchProDriver.cpp +++ b/src/drivers/switchpro/SwitchProDriver.cpp @@ -7,8 +7,6 @@ #define SWITCH_PRO_KEEPALIVE_TIMER 5 void SwitchProDriver::initialize() { - //stdio_init_all(); - playerID = 0; last_report_counter = 0; handshakeCounter = 0; diff --git a/src/hosts/DualshockPS4Host.cpp b/src/hosts/DualshockPS4Host.cpp index efe8c885d8..9d881081f5 100644 --- a/src/hosts/DualshockPS4Host.cpp +++ b/src/hosts/DualshockPS4Host.cpp @@ -1,18 +1,35 @@ #include "hosts/DualshockPS4Host.h" +#include "drivermanager.h" #include "storagemanager.h" // Static match function bool DualshockPS4Host::match(uint8_t dev_addr, uint8_t instance, uint16_t vendor_id, uint16_t product_id, uint8_t const* desc_report, uint16_t desc_len) { - // Victrix Pro FS + // Qanba Obisidian + if ( vendor_id == 0x2C22 ) { + switch(product_id) { + case 0x2200: // crystal + case 0x2500: // dragon + case 0x2000: // drone + case 0x2302: // obsidian + case 0x2702: // obsidian 2 + return true; + } + } - // Nacon Revolution Pro 3 + // Nacon Daija + if ( vendor_id == 0x146B ) { + switch(product_id) { + case 0x0D09: // PS4 mode + return true; + } + } // Hori (PS4) if ( vendor_id == 0x0F0D ) { switch(product_id) { - // Horipad FPS Plus? - // Hori Fighting Commander + case 0x008A: // we need someone to confirm this, real arcade pro V hayabusa ps4 mode + case 0x005E: case 0x00EE: return true; } @@ -71,54 +88,46 @@ void DualshockPS4Host::initialize(uint8_t dev_addr, uint8_t instance, uint16_t v _controller_host_state.ry = GAMEPAD_JOYSTICK_MID; memset(&prevReport, 0, sizeof(PS4Report)); + memset(&last_controller_output, 0, sizeof(PS4FeatureOutputReport)); - validPS4Definition = false; // check the report def - - // Find the report for PS4 definition if this controller has one - tuh_hid_report_info_t report_info[4]; - uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 4, desc_report, desc_len); - for(uint8_t i = 0; i < report_count; i++) { -#ifdef GAMEPAD_HOST_DEBUG - //printf("Report: %02x, Usage: %04x, Usage Page: %04x\n", report_info[i].report_id, report_info[i].usage_page, report_info[i].usage); -#endif - if (report_info[i].report_id == PS4AuthReport::PS4_DEFINITION) { - // controller is some other type that's not a DS4, so parse the config - memset(report_buffer, 0, PS4_ENDPOINT_SIZE); - report_buffer[0] = PS4AuthReport::PS4_DEFINITION; - tuh_hid_get_report(dev_addr, instance, PS4AuthReport::PS4_DEFINITION, HID_REPORT_TYPE_FEATURE, report_buffer, 48); - break; - } - } + tuh_hid_send_report(_dev_addr, _instance, PS4AuthReport::PS4_DEFINITION, &report_buffer, 64); } void DualshockPS4Host::update() { - if ( validPS4Definition == false ) return; - Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); PS4FeatureOutputReport controller_output; // this might have to be static memset(&controller_output, 0, sizeof(controller_output)); controller_output.reportID = PS4AuthReport::PS4_SET_FEATURE_STATE; - if (ds4Config.features.enableLED && gamepad->auxState.sensors.statusLight.enabled) { + + InputMode inputMode = DriverManager::getInstance().getInputMode(); + if (inputMode == INPUT_MODE_PS4 || inputMode == INPUT_MODE_PS5) { controller_output.enableUpdateLED = gamepad->auxState.sensors.statusLight.enabled; controller_output.ledRed = gamepad->auxState.sensors.statusLight.color.red; controller_output.ledGreen = gamepad->auxState.sensors.statusLight.color.green; controller_output.ledBlue = gamepad->auxState.sensors.statusLight.color.blue; - controller_output.ledBlinkOn = gamepad->auxState.playerID.ledBlinkOn; - controller_output.ledBlinkOff = gamepad->auxState.playerID.ledBlinkOff; - } - - if (ds4Config.features.enableRumble) { - gamepad->auxState.haptics.leftActuator.enabled = 1; - gamepad->auxState.haptics.rightActuator.enabled = 1; - controller_output.enableUpdateRumble = 1; - controller_output.rumbleLeft = gamepad->auxState.haptics.leftActuator.intensity; - controller_output.rumbleRight = gamepad->auxState.haptics.rightActuator.intensity; + if ( controller_output.ledBlinkOn > 0 || controller_output.ledBlinkOff > 0 ) { + controller_output.enableUpdateLEDBlink = true; + controller_output.ledBlinkOn = gamepad->auxState.playerID.ledBlinkOn; // time + controller_output.ledBlinkOff = gamepad->auxState.playerID.ledBlinkOff; // time + } + } else if ( inputMode == INPUT_MODE_XINPUT || inputMode == INPUT_MODE_XBONE || inputMode == INPUT_MODE_XBOXORIGINAL ) { + controller_output.enableUpdateLED = true; + controller_output.ledGreen = UINT8_MAX/2; + } else if ( inputMode == INPUT_MODE_SWITCH || inputMode == INPUT_MODE_SWITCH_PRO ) { + controller_output.enableUpdateLED = true; + controller_output.ledRed = UINT8_MAX/2; } - void * report = &controller_output; - uint16_t report_size = sizeof(controller_output)-1; + gamepad->auxState.haptics.leftActuator.enabled = true; + gamepad->auxState.haptics.rightActuator.enabled = true; + controller_output.enableUpdateRumble = true; + controller_output.rumbleLeft = gamepad->auxState.haptics.leftActuator.intensity; + controller_output.rumbleRight = gamepad->auxState.haptics.rightActuator.intensity; - tuh_hid_send_report(_dev_addr, _instance, PS4AuthReport::PS4_SET_FEATURE_STATE, (uint8_t*)report+1, report_size); + if ( memcmp(&controller_output, &last_controller_output, sizeof(PS4FeatureOutputReport)) != 0 ) { + tuh_hid_send_report(_dev_addr, _instance, 0, reinterpret_cast(&controller_output), sizeof(controller_output)); + memcpy(&last_controller_output, &controller_output, sizeof(PS4FeatureOutputReport)); + } } void DualshockPS4Host::process(uint8_t const* report, uint16_t len) { @@ -183,25 +192,6 @@ void DualshockPS4Host::gamepad(Gamepad * gamepad) { gamepad->state.lt = _controller_host_state.lt; } -void DualshockPS4Host::get_report_complete(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, uint16_t len) { - memcpy(&ds4Config, report_buffer+1, sizeof(PS4ControllerConfig)); - if ((ds4Config.hidUsage == 0x2721) || (ds4Config.hidUsage == 0x2127)) { - validPS4Definition = true; - #ifdef GAMEPAD_HOST_DEBUG - //printf("PS4 controller details\n"); - //printf("----------------------\n"); - //printf("enableController: %d\n", ds4Config.features.enableController); - //printf("enableMotion: %d\n", ds4Config.features.enableMotion); - //printf("enableLED: %d\n", ds4Config.features.enableLED); - //printf("enableRumble: %d\n", ds4Config.features.enableRumble); - //printf("enableAnalog: %d\n", ds4Config.features.enableAnalog); - //printf("enableUnknown0: %d\n", ds4Config.features.enableUnknown0); - //printf("enableTouchpad: %d\n", ds4Config.features.enableTouchpad); - //printf("enableUnknown1: %d\n", ds4Config.features.enableUnknown1); - #endif - } -} - uint32_t DualshockPS4Host::map(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } diff --git a/src/hosts/SwitchProHost.cpp b/src/hosts/SwitchProHost.cpp index 14701886a5..be71602096 100644 --- a/src/hosts/SwitchProHost.cpp +++ b/src/hosts/SwitchProHost.cpp @@ -57,8 +57,8 @@ void SwitchProHost::update() uint8_t report_size = 10; // no subcommand // rumble - gamepad->auxState.haptics.leftActuator.enabled = 1; - gamepad->auxState.haptics.rightActuator.enabled = 1; + gamepad->auxState.haptics.leftActuator.enabled = true; + gamepad->auxState.haptics.rightActuator.enabled = true; if (gamepad->auxState.haptics.leftActuator.active && gamepad->auxState.haptics.leftActuator.intensity > 0) { // personally I felt this a bit too strong on PS4 games, might need to be adjusted for personal preference @@ -73,7 +73,7 @@ void SwitchProHost::update() out_report.rumble_l[3] = 0x61; } if (gamepad->auxState.haptics.rightActuator.active - && gamepad->auxState.haptics.leftActuator.intensity > 0) { + && gamepad->auxState.haptics.rightActuator.intensity > 0) { uint8_t amplitude_r = static_cast(((gamepad->auxState.haptics.rightActuator.intensity / 255.0f) * 0.8f + 0.5f) * (0xC0 - 0x40) + 0x40); #ifdef GAMEPAD_HOST_DEBUG printf("Switch Pro Right Rumble Intensity: %d -> %d\n", gamepad->auxState.haptics.rightActuator.intensity, amplitude_r);