diff --git a/CMakeLists.txt b/CMakeLists.txt index 38e6a896e7..a8f3e87a03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,6 +304,13 @@ 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 +src/hosts/Xbox360Host.cpp ${PROTO_OUTPUT_DIR}/enums.pb.c ${PROTO_OUTPUT_DIR}/config.pb.c ) @@ -357,6 +364,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..492ace0cfa 100644 --- a/headers/addons/gamepad_usb_host_listener.h +++ b/headers/addons/gamepad_usb_host_listener.h @@ -4,122 +4,11 @@ #include "usblistener.h" #include "gamepad.h" #include "host/usbh.h" -#include "drivers/ps4/PS4Descriptors.h" -#include "drivers/switchpro/SwitchProDescriptors.h" -#define GAMEPAD_HOST_DEBUG false -#define GAMEPAD_HOST_USE_FEATURES true +#include "gphost.h" 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,75 +20,12 @@ 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 update(); 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) - 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 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); + uint8_t _controller_dev_addr; + uint8_t _controller_instance; + GPHost * _controller_host; + uint32_t _next_update; }; #endif diff --git a/headers/drivers/p5general/P5GeneralDescriptors.h b/headers/drivers/p5general/P5GeneralDescriptors.h index 529a66c5f3..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; @@ -70,7 +69,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/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/gphost.h b/headers/gphost.h new file mode 100644 index 0000000000..a54e1614f8 --- /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" +#include "gamepad.h" + +// +// 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; + 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; + 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..b7339b58bb --- /dev/null +++ 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(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); + 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 new file mode 100644 index 0000000000..a57fafaaad --- /dev/null +++ b/headers/hosts/DualsensePS5Host.h @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Copyright (c) 2026 OpenStickCommunity (gp2040-ce.info) + */ + +#ifndef _DUALSENSEPS5HOST_H_ +#define _DUALSENSEPS5HOST_H_ + +#include + +#include "drivers/p5general/P5GeneralDescriptors.h" + +// +// Dualsense PS5 Controller Host +// +class DualsensePS5Host : public GPHost { +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); + 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: + 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 // _DUALSENSEPS5HOST_H_ diff --git a/headers/hosts/DualshockPS4Host.h b/headers/hosts/DualshockPS4Host.h new file mode 100644 index 0000000000..1c7ee560c8 --- /dev/null +++ b/headers/hosts/DualshockPS4Host.h @@ -0,0 +1,40 @@ +/* + * 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(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); + 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 + PS4Report prevReport; + 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); + 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..ea4283f9e4 --- /dev/null +++ b/headers/hosts/GoogleStadiaHost.h @@ -0,0 +1,66 @@ +/* + * 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; + +// +// GP2040-CE USB Host Driver +// +class GoogleStadiaHost : public GPHost { +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); + 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 new file mode 100644 index 0000000000..e74400265f --- /dev/null +++ b/headers/hosts/SwitchProHost.h @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Copyright (c) 2026 OpenStickCommunity (gp2040-ce.info) + */ + +#ifndef _SWITCHPROHOST_H_ +#define _SWITCHPROHOST_H_ + +#include + +#include "drivers/switchpro/SwitchProDescriptors.h" +#include "drivers/switchpro/SwitchProDriver.h" + +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; + +// +// GP2040-CE USB Host Driver +// +class SwitchProHost : public GPHost { +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); + 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 new file mode 100644 index 0000000000..a7bca2ef67 --- /dev/null +++ b/headers/hosts/Ultrastik360Host.h @@ -0,0 +1,55 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: Copyright (c) 2026 OpenStickCommunity (gp2040-ce.info) + */ + +#ifndef _ULTRASTIK360HOST_H_ +#define _ULTRASTIK360HOST_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; + +// +// Ultrastik 360 Controller Host +// +class Ultrastik360Host : public GPHost { +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); + 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/headers/hosts/Xbox360Host.h b/headers/hosts/Xbox360Host.h new file mode 100644 index 0000000000..ea69771cf1 --- /dev/null +++ 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(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); + 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/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..2f29815a6e 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)->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 657bc7aa17..cf330633e5 100644 --- a/src/addons/gamepad_usb_host_listener.cpp +++ b/src/addons/gamepad_usb_host_listener.cpp @@ -3,948 +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/GoogleStadiaHost.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_enabled = false; -#if GAMEPAD_HOST_DEBUG - stdio_init_all(); -#endif + _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::update() { + if ( _controller_host == nullptr ) 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 - 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 + // Only host one controller at a time + if ( _controller_host != nullptr ) return; - 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; - - 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; - - case 0xC294: // Driving Force or similar - isDFInit = false; - setup_df_wheel(); - break; - case 0xC29A: - isDFInit = true; - break; + uint16_t vid = 0, pid = 0; + tuh_vid_pid_get(dev_addr, &vid, &pid); - 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; + _controller_host = nullptr; + if ( DualshockPS4Host::match(dev_addr, instance, vid, pid, desc_report, desc_len) ) { + _controller_host = new DualshockPS4Host(); + }else if ( DualsensePS5Host::match(dev_addr, instance, vid, pid, desc_report, desc_len) ) { + _controller_host = new DualsensePS5Host(); + } else if ( GoogleStadiaHost::match(dev_addr, instance, vid, pid, desc_report, desc_len) ) { + _controller_host = new GoogleStadiaHost(); + } else if ( DrivingForceHost::match(dev_addr, instance, vid, pid, desc_report, desc_len) ) { + _controller_host = new DrivingForceHost(); + } else if ( Ultrastik360Host::match(dev_addr, instance, vid, pid, desc_report, desc_len) ) { + _controller_host = new Ultrastik360Host(); + } 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? - /* 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 ( GenericHIDHost::match(vid, pid) ) { // do we want this one? + //controllerHost = new GenericHIDHost(); } -} - -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); - -#if GAMEPAD_HOST_DEBUG - printf("XMount: VID_%04x PID_%04x Type:%d Subtype:%d\n", controller_vid, controller_pid, controllerType, subtype); -#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; - 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 ( _controller_host != nullptr ) { + _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::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; -} +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; -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; + uint16_t vid, pid; + tuh_vid_pid_get(dev_addr, &vid, &pid); - // 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; + // Match is a static function, setup and init happens after we've established something + _controller_host = nullptr; + if ( Xbox360Host::match(dev_addr, instance, vid, pid, controllerType) ) { + _controller_host = new Xbox360Host(); } + /* else if ( XboxOneHost::match(vid, pid && controllerType == 2 ) { + _controller_host = new XboxOneHost(); + }*/ - // 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); -} - -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; - } -} + if ( _controller_host != nullptr ) { + _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 -// 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; + // if auto-magic + // check for xbox 360 markers + // check for xbox one markers } } -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); +void GamepadUSBHostListener::unmount(uint8_t dev_addr) { + if ( _controller_host == nullptr ) return; + + _controller_host->shutdown(); + delete _controller_host; + _controller_host = nullptr; } -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::report_received(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) { + if ( _controller_host != nullptr && + _controller_dev_addr == dev_addr && + _controller_instance == instance ) { + _controller_host->process(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; + 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 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; + 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/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/DrivingForceHost.cpp b/src/hosts/DrivingForceHost.cpp new file mode 100644 index 0000000000..4e7da10762 --- /dev/null +++ b/src/hosts/DrivingForceHost.cpp @@ -0,0 +1,97 @@ +#include "hosts/DrivingForceHost.h" + +#include "tusb_config.h" +#include "tusb.h" +#include "class/hid/hid.h" +#include "device/usbd_pvt.h" + +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 + case 0xC29A: + return true; + } + } + return false; +} + +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; + + _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 ( 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); + } +} + +void DrivingForceHost::process(uint8_t const* report, uint16_t len) { + PS3ReportAlt ps3Report; + memcpy(&ps3Report, report, len); +#ifdef 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 +/* + 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 new file mode 100644 index 0000000000..4c3678b249 --- /dev/null +++ b/src/hosts/DualsensePS5Host.cpp @@ -0,0 +1,93 @@ +#include "hosts/DualsensePS5Host.h" + +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) { + 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; + + _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(P5GenerorReport)); +} + +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 ( 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.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 == 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; + } + } + + 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 new file mode 100644 index 0000000000..9d881081f5 --- /dev/null +++ b/src/hosts/DualshockPS4Host.cpp @@ -0,0 +1,216 @@ +#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) { + // 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 Daija + if ( vendor_id == 0x146B ) { + switch(product_id) { + case 0x0D09: // PS4 mode + return true; + } + } + + // Hori (PS4) + if ( vendor_id == 0x0F0D ) { + switch(product_id) { + case 0x008A: // we need someone to confirm this, real arcade pro V hayabusa ps4 mode + case 0x005E: + 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; + } + } + + // 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; +} + +// 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)); + memset(&last_controller_output, 0, sizeof(PS4FeatureOutputReport)); + + tuh_hid_send_report(_dev_addr, _instance, PS4AuthReport::PS4_DEFINITION, &report_buffer, 64); +} + +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; + + 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; + 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; + } + + 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; + + 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) { + 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; +} + +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..0592b3c007 --- /dev/null +++ b/src/hosts/GoogleStadioHost.cpp @@ -0,0 +1,85 @@ +#include "hosts/GoogleStadiaHost.h" + +// Static match function +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) { + 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.lt = controller_report.SIM_GamePadBrake; + _controller_host_state.rt = controller_report.SIM_GamePadAccelerator; + + 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 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 new file mode 100644 index 0000000000..be71602096 --- /dev/null +++ b/src/hosts/SwitchProHost.cpp @@ -0,0 +1,256 @@ +#include "hosts/SwitchProHost.h" +#include "tusb_config.h" +#include "tusb.h" +#include "class/hid/hid.h" +#include "device/usbd_pvt.h" +#include "storagemanager.h" + +// Match +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) { + 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 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; + + switchProFinished = false; + switchReportCounter = 0; + switchProState = SwitchOutputSubtypes::IDENTIFY; + lastSwitchLed = 0; + memset(&prevReport, 0, sizeof(SwitchProReport)); +} + + +void SwitchProHost::update() +{ + if (!switchProFinished) return; + + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + + SwitchProHostReport out_report{ + .command = SwitchCommands::SPI_READ, + .counter = switchReportCounter++, + .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 = 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 + 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.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); +#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 = switchReportCounter++, + .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(_dev_addr, _instance, 0, &led_out_report, report_size); + } + lastSwitchLed = shifted; + } + + tuh_hid_send_report(_dev_addr, _instance, 0, &out_report, report_size); +} + +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; + + 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(&prevReport, &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); + + 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 new file mode 100644 index 0000000000..0310812162 --- /dev/null +++ b/src/hosts/Ultrastik360Host.cpp @@ -0,0 +1,53 @@ +#include "hosts/Ultrastik360Host.h" + +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) { + case 0x0510: // pre-2015 + case 0x0511: + return true; + } + //} + + return false; +} + +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; + + _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); + 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 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; +} diff --git a/src/hosts/Xbox360Host.cpp b/src/hosts/Xbox360Host.cpp new file mode 100644 index 0000000000..cf59955eba --- /dev/null +++ b/src/hosts/Xbox360Host.cpp @@ -0,0 +1,159 @@ +#include "hosts/Xbox360Host.h" +#include "drivers/shared/xinput_host.h" +#include + +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; +} + +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; + + _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; + + last_left_rumble = 0; + last_right_rumble = 0; + + memset(&prev_report, 0, sizeof(XInputReport)); + + 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); + } + + xinput_set_rumble(0, 0); + tuh_xinput_receive_report(_dev_addr, _instance); +} + +void Xbox360Host::update() { + Gamepad * gamepad = Storage::getInstance().GetProcessedGamepad(); + + // 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(leftRumble, rightRumble); +} + +void Xbox360Host::process(uint8_t const* report, uint16_t len) { + XInputReport controller_report; + + + + if (len < sizeof(XInputReport)) { +#ifdef 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; + + 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); + } +}