#pragma once #include "hid_pad_handler.h" #include namespace reports { constexpr u32 DUALSENSE_ACC_RES_PER_G = 8192; constexpr u32 DUALSENSE_GYRO_RES_PER_DEG_S = 86; // technically this could be 1024, but keeping it at 86 keeps us within 16 bits of precision constexpr u32 DUALSENSE_CALIBRATION_REPORT_SIZE = 41; constexpr u32 DUALSENSE_FIRMWARE_REPORT_SIZE = 64; constexpr u32 DUALSENSE_PAIRING_REPORT_SIZE = 20; constexpr u32 DUALSENSE_BLUETOOTH_REPORT_SIZE = 78; constexpr u32 DUALSENSE_USB_REPORT_SIZE = 63; // 64 in hid, because of the report ID being added constexpr u32 DUALSENSE_COMMON_REPORT_SIZE = 47; constexpr u32 DUALSENSE_USB_INPUT_REPORT_SIZE = 64; constexpr u32 DUALSENSE_BLUETOOTH_INPUT_REPORT_SIZE = 78; constexpr u32 DUALSENSE_INPUT_REPORT_GYRO_X_OFFSET = 15; constexpr u32 DUALSENSE_TOUCHPAD_WIDTH = 1920; constexpr u32 DUALSENSE_TOUCHPAD_HEIGHT = 1080; constexpr u32 DUALSENSE_TOUCH_POINT_INACTIVE = 0x80; enum { VALID_FLAG_0_COMPATIBLE_VIBRATION = 0x01, VALID_FLAG_0_HAPTICS_SELECT = 0x02, VALID_FLAG_0_SET_LEFT_TRIGGER_MOTOR = 0x04, VALID_FLAG_0_SET_RIGHT_TRIGGER_MOTOR = 0x08, VALID_FLAG_0_SET_AUDIO_VOLUME = 0x10, VALID_FLAG_0_TOGGLE_AUDIO = 0x20, VALID_FLAG_0_SET_MICROPHONE_VOLUME = 0x40, VALID_FLAG_0_TOGGLE_MICROPHONE = 0x80, VALID_FLAG_1_TOGGLE_MIC_BUTTON_LED = 0x01, VALID_FLAG_1_POWER_SAVE_CONTROL_ENABLE = 0x02, VALID_FLAG_1_LIGHTBAR_CONTROL_ENABLE = 0x04, VALID_FLAG_1_RELEASE_LEDS = 0x08, VALID_FLAG_1_PLAYER_INDICATOR_CONTROL_ENABLE = 0x10, VALID_FLAG_1_EFFECT_POWER = 0x40, VALID_FLAG_2_SET_PLAYER_LED_BRIGHTNESS = 0x01, VALID_FLAG_2_LIGHTBAR_SETUP_CONTROL_ENABLE = 0x02, VALID_FLAG_2_IMPROVED_RUMBLE_EMULATION = 0x04, AUDIO_BIT_FORCE_INTERNAL_MIC = 0x01, AUDIO_BIT_FORCE_EXTERNAL_MIC = 0x02, AUDIO_BIT_PAD_EXTERNAL_MIC = 0x04, AUDIO_BIT_PAD_INTERNAL_MIC = 0x08, AUDIO_BIT_DISABLE_EXTERNAL_SPEAKERS = 0x10, AUDIO_BIT_ENABLE_INTERNAL_SPEAKERS = 0x20, POWER_SAVE_CONTROL_MIC_MUTE = 0x10, POWER_SAVE_CONTROL_AUDIO_MUTE = 0x40, LIGHTBAR_SETUP_LIGHT_ON = 0x01, LIGHTBAR_SETUP_LIGHT_OFF = 0x02, MIC_BUTTON_LED_ON = 0x01, MIC_BUTTON_LED_PULSE = 0x02, }; struct dualsense_touch_point { u8 contact; u8 x_lo; u8 x_hi : 4; u8 y_lo : 4; u8 y_hi; }; static_assert(sizeof(dualsense_touch_point) == 4); struct dualsense_input_report_common { u8 x; u8 y; u8 rx; u8 ry; u8 z; u8 rz; u8 seq_number; u8 buttons[4]; u8 reserved[4]; le_t gyro[3]; le_t accel[3]; le_t sensor_timestamp; u8 reserved2; std::array points; u8 reserved3[12]; u8 status; u8 reserved4[10]; }; struct dualsense_input_report_usb { u8 report_id; dualsense_input_report_common common; }; static_assert(sizeof(dualsense_input_report_usb) == DUALSENSE_USB_INPUT_REPORT_SIZE); struct dualsense_input_report_bt { u8 report_id; u8 something; dualsense_input_report_common common; u8 reserved[9]; u8 crc32[4]; }; static_assert(sizeof(dualsense_input_report_bt) == DUALSENSE_BLUETOOTH_INPUT_REPORT_SIZE); struct dualsense_output_report_common { u8 valid_flag_0; u8 valid_flag_1; u8 motor_right; u8 motor_left; u8 headphone_volume; u8 speaker_volume; u8 microphone_volume; u8 audio_enable_bits; u8 mute_button_led; u8 power_save_control; u8 right_trigger_effect[11]; u8 left_trigger_effect[11]; u8 reserved[6]; u8 valid_flag_2; u8 effect_strength_1 : 4; // 0-7 (one for motors, the other for trigger motors, not sure which is which) u8 effect_strength_2 : 4; // 0-7 (one for motors, the other for trigger motors, not sure which is which) u8 reserved2; u8 lightbar_setup; u8 led_brightness; u8 player_leds; u8 lightbar_r; u8 lightbar_g; u8 lightbar_b; }; static_assert(sizeof(dualsense_output_report_common) == DUALSENSE_COMMON_REPORT_SIZE); struct dualsense_output_report_bt { u8 report_id; // 0x31 u8 seq_tag; u8 tag; dualsense_output_report_common common; u8 reserved[24]; u8 crc32[4]; }; static_assert(sizeof(dualsense_output_report_bt) == DUALSENSE_BLUETOOTH_REPORT_SIZE); struct dualsense_output_report_usb { u8 report_id; // 0x02 dualsense_output_report_common common; u8 reserved[15]; }; static_assert(sizeof(dualsense_output_report_usb) == DUALSENSE_USB_REPORT_SIZE); } // namespace reports class DualSenseDevice : public HidDevice { public: enum class DualSenseDataMode { Simple, Enhanced }; enum class DualSenseFeatureSet { Normal, Edge }; bool bt_controller{false}; u8 bt_sequence{0}; bool has_calib_data{false}; std::array calib_data{}; reports::dualsense_input_report_common report{}; // No need to have separate reports for usb and bluetooth DualSenseDataMode data_mode{DualSenseDataMode::Simple}; DualSenseFeatureSet feature_set{DualSenseFeatureSet::Normal}; bool init_lightbar{true}; bool update_lightbar{true}; bool release_leds{false}; // Controls for lightbar pulse. This seems somewhat hacky for now, as I haven't found out a nicer way. bool lightbar_on{false}; bool lightbar_on_old{false}; steady_clock::time_point last_lightbar_time; }; class dualsense_pad_handler final : public hid_pad_handler { enum DualSenseKeyCodes { None = 0, Triangle, Circle, Cross, Square, Left, Right, Up, Down, R1, R3, L1, L3, Share, Options, PSButton, Mic, TouchPad, Touch_L, Touch_R, Touch_U, Touch_D, L2, R2, LSXNeg, LSXPos, LSYNeg, LSYPos, RSXNeg, RSXPos, RSYNeg, RSYPos, EdgeFnL, EdgeFnR, EdgeLB, EdgeRB, }; public: dualsense_pad_handler(); ~dualsense_pad_handler(); void SetPadData(const std::string& padId, u8 player_id, u8 large_motor, u8 small_motor, s32 r, s32 g, s32 b, bool player_led, bool battery_led, u32 battery_led_brightness) override; u32 get_battery_level(const std::string& padId) override; void init_config(cfg_pad* cfg) override; private: bool get_calibration_data(DualSenseDevice* dev) const; DataStatus get_data(DualSenseDevice* device) override; void check_add_device(hid_device* hidDevice, hid_enumerated_device_view path, std::wstring_view wide_serial) override; int send_output_report(DualSenseDevice* device) override; bool get_is_left_trigger(const std::shared_ptr& device, u64 keyCode) override; bool get_is_right_trigger(const std::shared_ptr& device, u64 keyCode) override; bool get_is_left_stick(const std::shared_ptr& device, u64 keyCode) override; bool get_is_right_stick(const std::shared_ptr& device, u64 keyCode) override; bool get_is_touch_pad_motion(const std::shared_ptr& device, u64 keyCode) override; PadHandlerBase::connection update_connection(const std::shared_ptr& device) override; std::unordered_map get_button_values(const std::shared_ptr& device) override; pad_preview_values get_preview_values(const std::unordered_map& data) override; void get_extended_info(const pad_ensemble& binding) override; void apply_pad_data(const pad_ensemble& binding) override; };