diff --git a/bluetooth-mitm/source/controllers/dualsense_controller.cpp b/bluetooth-mitm/source/controllers/dualsense_controller.cpp index 6129068..bd2a25c 100644 --- a/bluetooth-mitm/source/controllers/dualsense_controller.cpp +++ b/bluetooth-mitm/source/controllers/dualsense_controller.cpp @@ -22,6 +22,51 @@ namespace ams::controller { const constexpr float stick_scale_factor = float(UINT12_MAX) / UINT8_MAX; + const uint8_t player_led_flags[] = { + // Mimic the Switch's player LEDs + 0x01, + 0x03, + 0x0B, + 0x1B, + 0x11, + 0x09, + 0x19, + 0x0A + }; + + const RGBColour player_led_colours[] = { + // Same colours used by PS4 + {0x00, 0x00, 0x40}, // blue + {0x40, 0x00, 0x00}, // red + {0x00, 0x40, 0x00}, // green + {0x20, 0x00, 0x20}, // pink + // New colours for controllers 5-8 + {0x00, 0x20, 0x20}, // cyan + {0x30, 0x10, 0x00}, // orange + {0x20, 0x20, 0x00}, // yellow + {0x10, 0x00, 0x30} // purple + }; + + } + + Result DualsenseController::Initialize(void) { + R_TRY(EmulatedSwitchController::Initialize()); + R_TRY(this->PushRumbleLedState()); + + return ams::ResultSuccess(); + } + + Result DualsenseController::SetPlayerLed(uint8_t led_mask) { + uint8_t player_number; + R_TRY(LedsMaskToPlayerNumber(led_mask, &player_number)); + m_led_flags = player_led_flags[player_number]; + RGBColour colour = player_led_colours[player_number]; + return this->SetLightbarColour(colour); + } + + Result DualsenseController::SetLightbarColour(RGBColour colour) { + m_led_colour = colour; + return this->PushRumbleLedState(); } void DualsenseController::UpdateControllerState(const bluetooth::HidReport *report) { @@ -31,12 +76,15 @@ namespace ams::controller { case 0x01: this->HandleInputReport0x01(dualsense_report); break; + case 0x31: + this->HandleInputReport0x31(dualsense_report); + break; default: break; } } - void DualsenseController::HandleInputReport0x01(const DualsenseReportData *src) { + void DualsenseController::HandleInputReport0x01(const DualsenseReportData *src) { m_left_stick = this->PackStickData( static_cast(stick_scale_factor * src->input0x01.left_stick.x) & 0xfff, static_cast(stick_scale_factor * (UINT8_MAX - src->input0x01.left_stick.y)) & 0xfff @@ -49,6 +97,32 @@ namespace ams::controller { this->MapButtons(&src->input0x01.buttons); } + void DualsenseController::HandleInputReport0x31(const DualsenseReportData *src) { + if (!src->input0x31.usb || src->input0x31.full) + m_charging = false; + else + m_charging = true; + + uint8_t battery_level = src->input0x31.battery_level; + if (!src->input0x31.usb) + battery_level++; + if (battery_level > 10) + battery_level = 10; + + m_battery = static_cast(8 * (battery_level + 1) / 10) & 0x0e; + + m_left_stick = this->PackStickData( + static_cast(stick_scale_factor * src->input0x31.left_stick.x) & 0xfff, + static_cast(stick_scale_factor * (UINT8_MAX - src->input0x31.left_stick.y)) & 0xfff + ); + m_right_stick = this->PackStickData( + static_cast(stick_scale_factor * src->input0x31.right_stick.x) & 0xfff, + static_cast(stick_scale_factor * (UINT8_MAX - src->input0x31.right_stick.y)) & 0xfff + ); + + this->MapButtons(&src->input0x31.buttons); + } + void DualsenseController::MapButtons(const DualsenseButtonData *buttons) { m_buttons.dpad_down = (buttons->dpad == DualsenseDPad_S) || (buttons->dpad == DualsenseDPad_SE) || @@ -83,4 +157,20 @@ namespace ams::controller { m_buttons.home = buttons->ps; } + Result DualsenseController::PushRumbleLedState(void) { + DualsenseOutputReport0x31 report = {0xa2, 0x31, 0x02, 0x00, 0x14}; + report.data[41] = 0x02; + report.data[44] = 0x02; + report.data[46] = m_led_flags; + report.data[47] = m_led_colour.r; + report.data[48] = m_led_colour.g; + report.data[49] = m_led_colour.b; + report.crc = crc32Calculate(report.data, sizeof(report.data)); + + s_output_report.size = sizeof(report) - 1; + std::memcpy(s_output_report.data, &report.data[1], s_output_report.size); + + return bluetooth::hid::report::SendHidReport(&m_address, &s_output_report); + } + } diff --git a/bluetooth-mitm/source/controllers/dualsense_controller.hpp b/bluetooth-mitm/source/controllers/dualsense_controller.hpp index c8e8593..b388622 100644 --- a/bluetooth-mitm/source/controllers/dualsense_controller.hpp +++ b/bluetooth-mitm/source/controllers/dualsense_controller.hpp @@ -56,18 +56,49 @@ namespace ams::controller { uint8_t counter : 6; } __attribute__((packed)); + struct DualsenseOutputReport0x31 { + struct { + uint8_t data[75]; + }; + uint32_t crc; + } __attribute__((packed)); + struct DualsenseInputReport0x01 { - DualsenseStickData left_stick; - DualsenseStickData right_stick; - DualsenseButtonData buttons; + DualsenseStickData left_stick; + DualsenseStickData right_stick; + DualsenseButtonData buttons; uint8_t left_trigger; uint8_t right_trigger; } __attribute__((packed)); + struct DualsenseInputReport0x31 { + uint8_t _unk0; + DualsenseStickData left_stick; + DualsenseStickData right_stick; + uint8_t left_trigger; + uint8_t right_trigger; + uint8_t counter; + DualsenseButtonData buttons; + uint8_t _unk1[5]; + uint16_t vel_x; + uint16_t vel_y; + uint16_t vel_z; + uint16_t acc_x; + uint16_t acc_y; + uint16_t acc_z; + uint8_t _unk2[25]; + + uint8_t battery_level : 4; + uint8_t usb : 1; + uint8_t full : 1; + uint8_t : 0; + } __attribute__((packed)); + struct DualsenseReportData { uint8_t id; union { DualsenseInputReport0x01 input0x01; + DualsenseInputReport0x31 input0x31; }; } __attribute__((packed)); @@ -79,15 +110,23 @@ namespace ams::controller { }; DualsenseController(const bluetooth::Address *address) - : EmulatedSwitchController(address) { }; + : EmulatedSwitchController(address), m_led_flags(0), m_led_colour({0, 0, 0}) { }; - //Result Initialize(void); + Result Initialize(void); + Result SetPlayerLed(uint8_t led_mask); + Result SetLightbarColour(RGBColour colour); void UpdateControllerState(const bluetooth::HidReport *report); private: void HandleInputReport0x01(const DualsenseReportData *src); + void HandleInputReport0x31(const DualsenseReportData *src); + void MapButtons(const DualsenseButtonData *buttons); + Result PushRumbleLedState(void); + + uint8_t m_led_flags; + RGBColour m_led_colour; }; }