From e22959174eef7c692586a603d6f459406230fae6 Mon Sep 17 00:00:00 2001 From: ndeadly <24677491+ndeadly@users.noreply.github.com> Date: Tue, 11 Apr 2023 15:56:48 +0200 Subject: [PATCH] mc.mitm: add support for dualshock3 controller --- README.md | 10 +- lib/Atmosphere-libs | 2 +- lib/libnx | 2 +- mc_mitm/config.ini | 2 + .../controllers/controller_management.cpp | 9 + .../controllers/controller_management.hpp | 2 + .../controllers/dualsense_controller.cpp | 4 +- .../controllers/dualshock3_controller.cpp | 292 ++++++++++++++++++ .../controllers/dualshock3_controller.hpp | 129 ++++++++ .../controllers/dualshock4_controller.cpp | 4 +- .../source/controllers/switch_controller.cpp | 8 +- .../source/controllers/switch_controller.hpp | 4 +- mc_mitm/source/mcmitm_config.cpp | 5 +- mc_mitm/source/mcmitm_config.hpp | 1 + mc_mitm/source/mcmitm_initialization.cpp | 9 + mc_mitm/source/mcmitm_main.cpp | 1 + mc_mitm/source/usb/mc_usb_handler.cpp | 88 ++++++ mc_mitm/source/usb/mc_usb_handler.hpp | 23 ++ 18 files changed, 579 insertions(+), 16 deletions(-) create mode 100644 mc_mitm/source/controllers/dualshock3_controller.cpp create mode 100644 mc_mitm/source/controllers/dualshock3_controller.hpp create mode 100644 mc_mitm/source/usb/mc_usb_handler.cpp create mode 100644 mc_mitm/source/usb/mc_usb_handler.hpp diff --git a/README.md b/README.md index dc30b78..5ec9357 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Use controllers from other consoles natively on your Nintendo Switch via Bluetoo * __Nintendo Wii Remote + extensions (Nunchuck, Classic Controller, Classic Controller Pro, SNES Mini, TaTaCon (Taiko drum), MotionPlus)__ * __Nintendo Wii Balance Board (experimental)__ * __Nintendo WiiU Pro Controller__ +* __Sony Dualshock3 (Playstation 3) Controller__ * __Sony DualShock4 (Playstation 4) Controller__ * __Sony Dualsense (Playstation 5) Controller__ * __Sony Dualsense Edge Controller__ @@ -101,6 +102,11 @@ Press the red sync button on the back of the controller. The controller LEDs wil It is recommended that you perform an analog stick calibration for these controller types where applicable, as every controller has different analog stick range and center position but unlike Switch controllers, there is no stored factory calibration. +***Sony Dualshock3 Controller*** +To pair this controller, you will need to connect it to the console via USB cable. Once the controller LEDs start flashing, disconnect the USB cable and hit the `PS` button. + +*Note: to avoid unwanted behaviour if using the controller in USB wired mode, this only works from the usual `Controllers->Change Grip/Order` screen.* + ***Sony Dualshock4/Dualsense Controllers*** Press and hold the `PS` + `share` buttons simultaneously until the lightbar starts blinking. When done correctly the blink pattern will resemble a heartbeat, otherwise it will blink on and off at a constant rate. @@ -132,6 +138,7 @@ These settings can be used to spoof your switch bluetooth to appear as another d - `[misc]` These are miscellaneous controller-specific settings etc. + - `dualshock3_led_mode` Set Dualshock 3 player LED behaviour. Valid modes [0-1] where 0=Switch pattern, 1=PS3 pattern - `dualshock4_polling_rate` Set polling rate for Sony Dualshock 4 controllers. Valid range [0-16] where 0=max, 16=min. Refer [here](https://github.com/ndeadly/MissionControl/blob/4a0326308d1ff39353b045f5efb1a99c4a504c28/mc_mitm/source/controllers/dualshock4_controller.hpp#L21) for corresponding frequency values. - `dualshock4_lightbar_brightness` Set LED lightbar brightness for Sony Dualshock 4 controllers. Valid range [0-9] where 0=off, 1=min, 2-9=12.5-100% in 12.5% increments. - `dualsense_lightbar_brightness` Set LED lightbar brightness for Sony Dualsense controllers. Valid range [0-9] where 0=off, 1=min, 2-9=12.5-100% in 12.5% increments. @@ -236,9 +243,6 @@ For more details on the various controller revisions (with images), see [here](h ***My Xbox One/Elite V2 controller used to connect and now it doesn't, what gives?*** As of late 2021, Microsoft introduced a new controller firmware that aims to bring Xbox One/Elite 2 controllers in line with the newer Series X|S controllers. Updating to this firmware switches the controller over to using Bluetooth Low Energy (LE), a newer bluetooth standard focused on low power consumption, which is not currently supported by Mission Control. If your controller firmware is version 5.xx.xxxx.x or above, you have the new LE firmware and will need to downgrade to the legacy one (see https://support.xbox.com/en-US/help/hardware-network/accessories/controller-firmware-reversion) -***Can you add support for PS3 (Dualshock3) controllers?*** -~~It's on my list of things to look into. The pairing process is non-standard and may require modifications to the Bluetooth driver. If it can be done non-destructively I will add support eventually.~~ Having looked into this a bit, it appears it's going to be a lot of work given the constraints of HOS (if it can even be done without breaking support for other Bluetooth controllers). I won't rule out a solution in the future, but this is not high priority for me right now. Sorry DS3 owners. - ***Can you add support for Xbox 360 controllers?*** No, not currently. These don't use Bluetooth. Try sys-con with a wireless USB adapter. diff --git a/lib/Atmosphere-libs b/lib/Atmosphere-libs index af0d008..b1607dc 160000 --- a/lib/Atmosphere-libs +++ b/lib/Atmosphere-libs @@ -1 +1 @@ -Subproject commit af0d008900128d8679b80569f69fe562ed7d6681 +Subproject commit b1607dc8a3d85bc8c859c60d70ebb4a3dcbb85b8 diff --git a/lib/libnx b/lib/libnx index 03007f7..db592d8 160000 --- a/lib/libnx +++ b/lib/libnx @@ -1 +1 @@ -Subproject commit 03007f7b9b1e16d9bc5fb239be1980e077288749 +Subproject commit db592d8aebd047cabf8925b25fbf20d08d1c0495 diff --git a/mc_mitm/config.ini b/mc_mitm/config.ini index 15be267..b3e8325 100644 --- a/mc_mitm/config.ini +++ b/mc_mitm/config.ini @@ -11,6 +11,8 @@ ;host_address=04:20:69:04:20:69 [misc] +; Set Dualshock 3 player LED behaviour. Valid modes [0-1] where 0=Switch pattern, 1=PS3 pattern [default 0] +;dualshock3_led_mode=0 ; Set polling rate for Sony Dualshock 4 controllers. Valid range [0-16] where 0=max, 16=min [default 8 (125Hz)] ; Refer to https://github.com/ndeadly/MissionControl/blob/4a0326308d1ff39353b045f5efb1a99c4a504c28/mc_mitm/source/controllers/dualshock4_controller.hpp#L21 ;dualshock4_polling_rate=8 diff --git a/mc_mitm/source/controllers/controller_management.cpp b/mc_mitm/source/controllers/controller_management.cpp index fb9dd75..b797049 100644 --- a/mc_mitm/source/controllers/controller_management.cpp +++ b/mc_mitm/source/controllers/controller_management.cpp @@ -54,6 +54,12 @@ namespace ams::controller { } } + for (auto hwId : Dualshock3Controller::hardware_ids) { + if ( (device->vid == hwId.vid) && (device->pid == hwId.pid) ) { + return ControllerType_Dualshock3; + } + } + for (auto hwId : Dualshock4Controller::hardware_ids) { if ( (device->vid == hwId.vid) && (device->pid == hwId.pid) ) { return ControllerType_Dualshock4; @@ -206,6 +212,9 @@ namespace ams::controller { case ControllerType_Wii: controller = std::make_shared(address, id); break; + case ControllerType_Dualshock3: + controller = std::make_shared(address, id); + break; case ControllerType_Dualshock4: controller = std::make_shared(address, id); break; diff --git a/mc_mitm/source/controllers/controller_management.hpp b/mc_mitm/source/controllers/controller_management.hpp index ac9c29c..a406bbb 100644 --- a/mc_mitm/source/controllers/controller_management.hpp +++ b/mc_mitm/source/controllers/controller_management.hpp @@ -19,6 +19,7 @@ #include "switch_controller.hpp" #include "wii_controller.hpp" +#include "dualshock3_controller.hpp" #include "dualshock4_controller.hpp" #include "dualsense_controller.hpp" #include "xbox_one_controller.hpp" @@ -48,6 +49,7 @@ namespace ams::controller { enum ControllerType { ControllerType_Switch, ControllerType_Wii, + ControllerType_Dualshock3, ControllerType_Dualshock4, ControllerType_Dualsense, ControllerType_XboxOne, diff --git a/mc_mitm/source/controllers/dualsense_controller.cpp b/mc_mitm/source/controllers/dualsense_controller.cpp index d020421..c6fd925 100644 --- a/mc_mitm/source/controllers/dualsense_controller.cpp +++ b/mc_mitm/source/controllers/dualsense_controller.cpp @@ -254,7 +254,7 @@ namespace ams::controller { Result DualsenseController::GetVersionInfo(DualsenseVersionInfo *version_info) { bluetooth::HidReport output; - R_TRY(this->GetFeatureReport(0x20, &output)); + R_TRY(this->GetReport(0x20, BtdrvBluetoothHhReportType_Feature, &output)); auto response = reinterpret_cast(&output.data); std::memcpy(version_info, &response->feature0x20.version_info, sizeof(DualsenseVersionInfo)); @@ -264,7 +264,7 @@ namespace ams::controller { Result DualsenseController::GetCalibrationData(DualsenseImuCalibrationData *calibration) { bluetooth::HidReport output; - R_TRY(this->GetFeatureReport(0x05, &output)); + R_TRY(this->GetReport(0x05, BtdrvBluetoothHhReportType_Feature, &output)); auto response = reinterpret_cast(&output.data); std::memcpy(calibration, &response->feature0x05.calibration, sizeof(DualsenseImuCalibrationData)); diff --git a/mc_mitm/source/controllers/dualshock3_controller.cpp b/mc_mitm/source/controllers/dualshock3_controller.cpp new file mode 100644 index 0000000..fed39c1 --- /dev/null +++ b/mc_mitm/source/controllers/dualshock3_controller.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2020-2023 ndeadly + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "dualshock3_controller.hpp" +#include "../bluetooth_mitm/bluetooth/bluetooth_core.hpp" +#include "../mcmitm_config.hpp" +#include +#include + +namespace ams::controller { + + namespace { + + const char *ds3_device_name = "PLAYSTATION(R)3 Controller"; + constexpr u16 ds3_vendor_id = 0x054c; + constexpr u16 ds3_product_id = 0x0268; + + enum Dualshock3LedMode { + Dualshock3LedMode_Switch, + Dualshock3LedMode_Ps3 + }; + + const u8 enable_payload[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 }; + const u8 led_config[] = { 0xff, 0x27, 0x10, 0x00, 0x32 }; + const u8 player_led_patterns[] = { 0b1000, 0b1100, 0b1110, 0b1111, 0b1001, 0b0101, 0b1101, 0b0110 }; + + constexpr float stick_scale_factor = float(UINT12_MAX) / UINT8_MAX; + + alignas(os::MemoryPageSize) constinit u8 g_usb_buffer[0x1000]; + + Result SetMasterAddress(UsbHsClientIfSession *if_session, const BtdrvAddress *address) { + const struct { + u8 unk1; + u8 unk2; + BtdrvAddress address; + } data = {0x01, 0x00, *address}; + + std::memcpy(&g_usb_buffer, &data, sizeof(data)); + + u32 rx_size = 0; + Result rc = usbHsIfCtrlXfer(if_session, + USB_ENDPOINT_OUT | (0x01 << 5) | 0x01, + USB_REQUEST_SET_CONFIGURATION, + 0x3f5, + 0, + sizeof(data), + &g_usb_buffer, + &rx_size + ); + + R_RETURN(rc); + } + + Result GetSlaveAddress(UsbHsClientIfSession *if_session, BtdrvAddress *address) { + u32 tx_size = 0; + Result rc = usbHsIfCtrlXfer(if_session, + USB_ENDPOINT_IN | (0x01 << 5) | 0x01, + USB_REQUEST_CLEAR_FEATURE, + 0x3f2, + 0, + 18, + &g_usb_buffer, + &tx_size + ); + + if (R_SUCCEEDED(rc)) + *address = *reinterpret_cast(&g_usb_buffer[4]); + + R_RETURN(rc); + } + + Result TrustDevice(const BtdrvAddress *address) { + SetSysBluetoothDevicesSettings device = {}; + device.addr = *address; + device.class_of_device = {0x00, 0x05, 0x08}; + device.link_key_present = false; + device.trusted_services = 0x100000; + device.vid = ds3_vendor_id; + device.pid = ds3_product_id; + device.sub_class = 0x08; + device.attribute_mask = 0xff; + + if (hos::GetVersion() < hos::Version_13_0_0) { + std::strncpy(device.name.name, ds3_device_name, sizeof(device.name)); + } + else { + std::strncpy(device.name2, ds3_device_name, sizeof(device.name2)); + } + + R_RETURN(btdrvAddPairedDeviceInfo(&device)); + } + + void SignalBondComplete(const BtdrvAddress *address) { + if (hos::GetVersion() < hos::Version_9_0_0) { + const struct { + BtdrvAddress addr; + u8 pad[2]; + u32 status; + u32 type; + } bond_event = { *address, {0}, 0, BtdrvConnectionEventType_Suspended }; + + bluetooth::core::SignalFakeEvent(BtdrvEventTypeOld_Connection, &bond_event, sizeof(bond_event)); + } else if (hos::GetVersion() < hos::Version_12_0_0) { + const struct { + u32 status; + BtdrvAddress addr; + u8 pad[2]; + u32 type; + } bond_event = { 0, *address, {0}, BtdrvConnectionEventType_Suspended }; + + bluetooth::core::SignalFakeEvent(BtdrvEventTypeOld_Connection, &bond_event, sizeof(bond_event)); + } else { + const struct { + u32 type; + BtdrvAddress addr; + u8 reserved[0xfe]; + } bond_event = { BtdrvConnectionEventType_Suspended, *address, {0} }; + + bluetooth::core::SignalFakeEvent(BtdrvEventType_Connection, &bond_event, sizeof(bond_event)); + } + } + + } + + bool Dualshock3Controller::UsbIdentify(UsbHsInterface *iface) { + return (iface->device_desc.idVendor == ds3_vendor_id) && (iface->device_desc.idProduct == ds3_product_id); + } + + Result Dualshock3Controller::UsbPair(UsbHsInterface *iface) { + // Acquire usb:hs client interface session + UsbHsClientIfSession if_session; + R_TRY(usbHsAcquireUsbIf(&if_session, iface)); + + // Close session on function exit + ON_SCOPE_EXIT { + if (usbHsIfIsActive(&if_session)) { + usbHsIfClose(&if_session); + } + }; + + // Fetch the console bluetooth address + BtdrvAdapterProperty property; + R_TRY(btdrvGetAdapterProperty(BtdrvAdapterPropertyType_Address, &property)); + + // Set the console address as the master on the DS3 + BtdrvAddress master_address = *reinterpret_cast(property.data); + R_TRY(SetMasterAddress(&if_session, &master_address)); + + // Get the address of the DS3 + BtdrvAddress slave_address; + R_TRY(GetSlaveAddress(&if_session, &slave_address)); + + // Add DS3 to list of trusted devices + R_TRY(TrustDevice(&slave_address)); + + // Signal fake bonding success event for btm + SignalBondComplete(&slave_address); + + R_SUCCEED(); + } + + Result Dualshock3Controller::Initialize() { + R_TRY(EmulatedSwitchController::Initialize()); + R_TRY(this->SendEnablePayload()); + + R_SUCCEED(); + } + + Result Dualshock3Controller::SetVibration(const SwitchRumbleData *rumble_data) { + m_rumble_state.amp_motor_left = static_cast(255 * std::max(rumble_data[0].low_band_amp, rumble_data[1].low_band_amp)); + m_rumble_state.amp_motor_right = static_cast(255 * std::max(rumble_data[0].high_band_amp, rumble_data[1].high_band_amp)); + R_RETURN(this->PushRumbleLedState()); + } + + Result Dualshock3Controller::CancelVibration() { + m_rumble_state.amp_motor_left = 0; + m_rumble_state.amp_motor_right = 0; + R_RETURN(this->PushRumbleLedState()); + } + + Result Dualshock3Controller::SetPlayerLed(u8 led_mask) { + u8 player_index; + R_TRY(LedsMaskToPlayerNumber(led_mask, &player_index)); + + auto config = mitm::GetGlobalConfig(); + if (config->misc.dualshock3_led_mode == Dualshock3LedMode_Ps3) { + if (player_index < 4) { + m_led_mask = 1 << player_index; + } else { + m_led_mask = ~(1 << player_index) & 0x0f; + } + } else { + m_led_mask = player_led_patterns[player_index]; + } + R_RETURN(this->PushRumbleLedState()); + } + + void Dualshock3Controller::ProcessInputData(const bluetooth::HidReport *report) { + auto ds3_report = reinterpret_cast(&report->data); + + switch(ds3_report->id) { + case 0x01: + this->MapInputReport0x01(ds3_report); break; + default: + break; + } + } + + void Dualshock3Controller::MapInputReport0x01(const Dualshock3ReportData *src) { + m_left_stick.SetData( + static_cast(stick_scale_factor * src->input0x01.left_stick.x) & UINT12_MAX, + static_cast(stick_scale_factor * (UINT8_MAX - src->input0x01.left_stick.y)) & UINT12_MAX + ); + m_right_stick.SetData( + static_cast(stick_scale_factor * src->input0x01.right_stick.x) & UINT12_MAX, + static_cast(stick_scale_factor * (UINT8_MAX - src->input0x01.right_stick.y)) & UINT12_MAX + ); + + m_buttons.dpad_down = src->input0x01.buttons.dpad_down; + m_buttons.dpad_up = src->input0x01.buttons.dpad_up; + m_buttons.dpad_right = src->input0x01.buttons.dpad_right; + m_buttons.dpad_left = src->input0x01.buttons.dpad_left; + + m_buttons.A = src->input0x01.buttons.circle; + m_buttons.B = src->input0x01.buttons.cross; + m_buttons.X = src->input0x01.buttons.triangle; + m_buttons.Y = src->input0x01.buttons.square; + + m_buttons.R = src->input0x01.buttons.R1; + m_buttons.ZR = src->input0x01.buttons.R2; + m_buttons.L = src->input0x01.buttons.L1; + m_buttons.ZL = src->input0x01.buttons.L2; + + m_buttons.minus = src->input0x01.buttons.select; + m_buttons.plus = src->input0x01.buttons.start; + + m_buttons.lstick_press = src->input0x01.buttons.L3; + m_buttons.rstick_press = src->input0x01.buttons.R3; + + m_buttons.home = src->input0x01.buttons.ps; + + m_charging = src->input0x01.charge == 0x02; + m_battery = std::clamp(src->input0x01.battery, 0, 4) * 2; + + // Workaround for controller reporting battery empty and being disconnected under certain conditions + if (m_battery == 0) { + m_battery = 1; + } + } + + Result Dualshock3Controller::SendEnablePayload() { + m_output_report.size = sizeof(enable_payload); + std::memcpy(m_output_report.data, enable_payload, m_output_report.size); + + R_RETURN(this->SetReport(BtdrvBluetoothHhReportType_Feature, &m_output_report)); + } + + Result Dualshock3Controller::PushRumbleLedState() { + std::scoped_lock lk(m_output_mutex); + + Dualshock3ReportData report = {}; + report.id = 0x01; + report.output0x01.data[1] = 10; + report.output0x01.data[2] = m_rumble_state.amp_motor_right ? 1 : 0; + report.output0x01.data[3] = 10; + report.output0x01.data[4] = m_rumble_state.amp_motor_left; + report.output0x01.data[9] = m_led_mask << 1; + std::memcpy(&report.output0x01.data[10], led_config, sizeof(led_config)); + std::memcpy(&report.output0x01.data[15], led_config, sizeof(led_config)); + std::memcpy(&report.output0x01.data[20], led_config, sizeof(led_config)); + std::memcpy(&report.output0x01.data[25], led_config, sizeof(led_config)); + + m_output_report.size = sizeof(report.output0x01) + sizeof(report.id); + std::memcpy(m_output_report.data, &report, m_output_report.size); + + R_RETURN(this->SetReport(BtdrvBluetoothHhReportType_Output, &m_output_report)); + } + +} diff --git a/mc_mitm/source/controllers/dualshock3_controller.hpp b/mc_mitm/source/controllers/dualshock3_controller.hpp new file mode 100644 index 0000000..901f902 --- /dev/null +++ b/mc_mitm/source/controllers/dualshock3_controller.hpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2020-2023 ndeadly + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "emulated_switch_controller.hpp" + +namespace ams::controller { + + struct Dualshock3StickData { + u8 x; + u8 y; + } PACKED; + + struct Dualshock3ButtonData { + u8 select : 1; + u8 L3 : 1; + u8 R3 : 1; + u8 start : 1; + u8 dpad_up : 1; + u8 dpad_right : 1; + u8 dpad_down : 1; + u8 dpad_left : 1; + + u8 L2 : 1; + u8 R2 : 1; + u8 L1 : 1; + u8 R1 : 1; + u8 triangle : 1; + u8 circle : 1; + u8 cross : 1; + u8 square : 1; + + u8 ps : 1; + u8 : 0; + } PACKED; + + struct Dualshock3RumbleData { + u8 amp_motor_left; + u8 amp_motor_right; + } PACKED; + + struct Dualshock3InputReport0x01 { + u8 unk0; + Dualshock3ButtonData buttons; + u8 unk1; + Dualshock3StickData left_stick; + Dualshock3StickData right_stick; + u8 unk2[4]; + u8 pressure_dpad_up; + u8 pressure_dpad_right; + u8 pressure_dpad_down; + u8 pressure_dpad_left; + u8 left_trigger; + u8 right_trigger; + u8 unk3[2]; + u8 pressure_triangle; + u8 pressure_circle; + u8 pressure_cross; + u8 pressure_square; + u8 unk4[3]; + u8 charge; + u8 battery; + u8 connection; + u8 unk5[9]; + u16 accel_x; + u16 accel_y; + u16 accel_z; + u16 gyro_x; + } PACKED; + + struct Dualshock3OutputReport0x01 { + struct { + u8 data[35]; + }; + } PACKED; + + struct Dualshock3ReportData { + u8 id; + union { + Dualshock3InputReport0x01 input0x01; + Dualshock3OutputReport0x01 output0x01; + }; + } PACKED; + + class Dualshock3Controller final : public EmulatedSwitchController { + + public: + static constexpr const HardwareID hardware_ids[] = { + {0x054c, 0x0268}, // Official Dualshock3 + }; + + static bool UsbIdentify(UsbHsInterface *iface); + static Result UsbPair(UsbHsInterface *iface); + + public: + Dualshock3Controller(const bluetooth::Address *address, HardwareID id) + : EmulatedSwitchController(address, id) { } + + Result Initialize(void); + Result SetVibration(const SwitchRumbleData *rumble_data); + Result CancelVibration(); + Result SetPlayerLed(u8 led_mask); + + void ProcessInputData(const bluetooth::HidReport *report) override; + + private: + void MapInputReport0x01(const Dualshock3ReportData *src); + + Result SendEnablePayload(void); + Result PushRumbleLedState(); + + u8 m_led_mask; + Dualshock3RumbleData m_rumble_state; + }; + +} diff --git a/mc_mitm/source/controllers/dualshock4_controller.cpp b/mc_mitm/source/controllers/dualshock4_controller.cpp index 5857fdc..3fc77c3 100644 --- a/mc_mitm/source/controllers/dualshock4_controller.cpp +++ b/mc_mitm/source/controllers/dualshock4_controller.cpp @@ -217,7 +217,7 @@ namespace ams::controller { Result Dualshock4Controller::GetVersionInfo(Dualshock4VersionInfo *version_info) { bluetooth::HidReport output; - R_TRY(this->GetFeatureReport(0x06, &output)); + R_TRY(this->GetReport(0x06, BtdrvBluetoothHhReportType_Feature, &output)); auto response = reinterpret_cast(&output.data); std::memcpy(version_info, &response->feature0x06.version_info, sizeof(Dualshock4VersionInfo)); @@ -227,7 +227,7 @@ namespace ams::controller { Result Dualshock4Controller::GetCalibrationData(Dualshock4ImuCalibrationData *calibration) { bluetooth::HidReport output; - R_TRY(this->GetFeatureReport(0x05, &output)); + R_TRY(this->GetReport(0x05, BtdrvBluetoothHhReportType_Feature, &output)); auto response = reinterpret_cast(&output.data); std::memcpy(calibration, &response->feature0x05.calibration, sizeof(Dualshock4ImuCalibrationData)); diff --git a/mc_mitm/source/controllers/switch_controller.cpp b/mc_mitm/source/controllers/switch_controller.cpp index 813afc2..d590847 100644 --- a/mc_mitm/source/controllers/switch_controller.cpp +++ b/mc_mitm/source/controllers/switch_controller.cpp @@ -167,12 +167,12 @@ namespace ams::controller { R_SUCCEED(); } - Result SwitchController::SetFeatureReport(const bluetooth::HidReport *report) { + Result SwitchController::SetReport(BtdrvBluetoothHhReportType type, const bluetooth::HidReport *report) { auto response = std::make_shared(BtdrvHidEventType_SetReport); m_future_responses.push(response); ON_SCOPE_EXIT { m_future_responses.pop(); }; - R_TRY(btdrvSetHidReport(m_address, BtdrvBluetoothHhReportType_Feature, report)); + R_TRY(btdrvSetHidReport(m_address, type, report)); if (!response->TimedWait(ams::TimeSpan::FromMilliSeconds(500))) { return -1; // This should return a proper failure code @@ -183,12 +183,12 @@ namespace ams::controller { return response_data.set_report.res; } - Result SwitchController::GetFeatureReport(u8 id, bluetooth::HidReport *out_report) { + Result SwitchController::GetReport(u8 id, BtdrvBluetoothHhReportType type, bluetooth::HidReport *out_report) { auto response = std::make_shared(BtdrvHidEventType_GetReport); m_future_responses.push(response); ON_SCOPE_EXIT { m_future_responses.pop(); }; - R_TRY(btdrvGetHidReport(m_address, id, BtdrvBluetoothHhReportType_Feature)); + R_TRY(btdrvGetHidReport(m_address, id, type)); if (!response->TimedWait(ams::TimeSpan::FromMilliSeconds(500))) { return -1; // This should return a proper failure code diff --git a/mc_mitm/source/controllers/switch_controller.hpp b/mc_mitm/source/controllers/switch_controller.hpp index eafabba..0f83a0b 100644 --- a/mc_mitm/source/controllers/switch_controller.hpp +++ b/mc_mitm/source/controllers/switch_controller.hpp @@ -358,8 +358,8 @@ namespace ams::controller { protected: Result WriteDataReport(const bluetooth::HidReport *report); Result WriteDataReport(const bluetooth::HidReport *report, u8 response_id, bluetooth::HidReport *out_report); - Result SetFeatureReport(const bluetooth::HidReport *report); - Result GetFeatureReport(u8 id, bluetooth::HidReport *out_report); + Result SetReport(BtdrvBluetoothHhReportType type, const bluetooth::HidReport *report); + Result GetReport(u8 id, BtdrvBluetoothHhReportType type, bluetooth::HidReport *out_report); virtual void UpdateControllerState(const bluetooth::HidReport *report); virtual void ApplyButtonCombos(SwitchButtonData *buttons); diff --git a/mc_mitm/source/mcmitm_config.cpp b/mc_mitm/source/mcmitm_config.cpp index 0a2bc1c..010d01b 100644 --- a/mc_mitm/source/mcmitm_config.cpp +++ b/mc_mitm/source/mcmitm_config.cpp @@ -30,6 +30,7 @@ namespace ams::mitm { .enable_motion = true }, .misc = { + .dualshock3_led_mode = 0, .dualshock4_polling_rate = 8, .dualshock4_lightbar_brightness = 5, .dualsense_lightbar_brightness = 5, @@ -91,7 +92,9 @@ namespace ams::mitm { ParseBluetoothAddress(value, &config->bluetooth.host_address); } } else if (strcasecmp(section, "misc") == 0) { - if (strcasecmp(name, "dualshock4_polling_rate") == 0) { + if (strcasecmp(name, "dualshock3_led_mode") == 0) { + ParseInt(value, &config->misc.dualshock3_led_mode, 0, 1); + } else if (strcasecmp(name, "dualshock4_polling_rate") == 0) { ParseInt(value, &config->misc.dualshock4_polling_rate, 0, 16); } else if (strcasecmp(name, "dualshock4_lightbar_brightness") == 0) { ParseInt(value, &config->misc.dualshock4_lightbar_brightness, 0, 9); diff --git a/mc_mitm/source/mcmitm_config.hpp b/mc_mitm/source/mcmitm_config.hpp index 732c1a3..c5ea571 100644 --- a/mc_mitm/source/mcmitm_config.hpp +++ b/mc_mitm/source/mcmitm_config.hpp @@ -29,6 +29,7 @@ namespace ams::mitm { } bluetooth; struct { + int dualshock3_led_mode; int dualshock4_polling_rate; int dualshock4_lightbar_brightness; int dualsense_lightbar_brightness; diff --git a/mc_mitm/source/mcmitm_initialization.cpp b/mc_mitm/source/mcmitm_initialization.cpp index c852e18..d41ee0f 100644 --- a/mc_mitm/source/mcmitm_initialization.cpp +++ b/mc_mitm/source/mcmitm_initialization.cpp @@ -27,6 +27,7 @@ #include "bluetooth_mitm/bluetooth/bluetooth_hid.hpp" #include "bluetooth_mitm/bluetooth/bluetooth_hid_report.hpp" #include "bluetooth_mitm/bluetooth/bluetooth_ble.hpp" +#include "usb/mc_usb_handler.hpp" namespace ams::mitm { @@ -88,6 +89,12 @@ namespace ams::mitm { } g_init_event.Signal(); + + // Loop until we can initialise btm:sys + while (R_FAILED(btmsysInitialize())) { + os::SleepThread(ams::TimeSpan::FromMilliSeconds(200)); + } + } } @@ -113,9 +120,11 @@ namespace ams::mitm { R_ABORT_UNLESS(ams::mitm::bluetooth::Launch()); R_ABORT_UNLESS(ams::mitm::btm::Launch()); R_ABORT_UNLESS(ams::mitm::mc::Launch()); + R_ABORT_UNLESS(ams::usb::Launch()); } void WaitModules() { + ams::usb::WaitFinished(); ams::mitm::mc::WaitFinished(); ams::mitm::btm::WaitFinished(); ams::mitm::bluetooth::WaitFinished(); diff --git a/mc_mitm/source/mcmitm_main.cpp b/mc_mitm/source/mcmitm_main.cpp index c256f61..b55b310 100644 --- a/mc_mitm/source/mcmitm_main.cpp +++ b/mc_mitm/source/mcmitm_main.cpp @@ -66,6 +66,7 @@ namespace ams { R_ABORT_UNLESS(pmdmntInitialize()); R_ABORT_UNLESS(pminfoInitialize()); + R_ABORT_UNLESS(usbHsInitialize()); R_ABORT_UNLESS(fs::MountSdCard("sdmc")); } diff --git a/mc_mitm/source/usb/mc_usb_handler.cpp b/mc_mitm/source/usb/mc_usb_handler.cpp new file mode 100644 index 0000000..cd9ef91 --- /dev/null +++ b/mc_mitm/source/usb/mc_usb_handler.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020-2023 ndeadly + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "mc_usb_handler.hpp" +#include "../controllers/dualshock3_controller.hpp" + +namespace ams::usb { + + namespace { + + const UsbHsInterfaceFilter g_if_filter = { + .Flags = UsbHsInterfaceFilterFlags_bcdDevice_Min | UsbHsInterfaceFilterFlags_bInterfaceClass, + .bcdDevice_Min = 0, + .bInterfaceClass = USB_CLASS_HID, + }; + + Result HandleUsbHsInterfaceAvailableEvent() { + s32 total_entries = 0; + UsbHsInterface interfaces[8] = {}; + R_TRY(usbHsQueryAvailableInterfaces(&g_if_filter, interfaces, sizeof(interfaces), &total_entries)); + + for(int i = 0; i < total_entries; ++i) { + if (controller::Dualshock3Controller::UsbIdentify(&interfaces[i])) { + bool pairing_started = false; + R_TRY(btmsysIsGamepadPairingStarted(&pairing_started)); + if (pairing_started) { + R_TRY(controller::Dualshock3Controller::UsbPair(&interfaces[i])); + } + } + } + + R_SUCCEED(); + } + + const s32 ThreadPriority = 9; + const size_t ThreadStackSize = 0x2000; + alignas(os::ThreadStackAlignment) u8 g_thread_stack[ThreadStackSize]; + os::ThreadType g_thread; + + void UsbThreadFunction(void *) { + Event if_event; + R_ABORT_UNLESS(usbHsCreateInterfaceAvailableEvent(&if_event, true, 0, &g_if_filter)); + + os::SystemEvent interface_available_event; + interface_available_event.AttachReadableHandle(if_event.revent, false, os::EventClearMode_AutoClear); + + for (;;) { + interface_available_event.Wait(); + R_ABORT_UNLESS(HandleUsbHsInterfaceAvailableEvent()); + } + + usbHsDestroyInterfaceAvailableEvent(&if_event, 0); + } + + } + + Result Launch() { + R_TRY(os::CreateThread(&g_thread, + UsbThreadFunction, + nullptr, + g_thread_stack, + ThreadStackSize, + ThreadPriority + )); + + os::SetThreadNamePointer(&g_thread, "mc::UsbThread"); + os::StartThread(&g_thread); + + return ams::ResultSuccess(); + } + + void WaitFinished() { + os::WaitThread(&g_thread); + } + +} diff --git a/mc_mitm/source/usb/mc_usb_handler.hpp b/mc_mitm/source/usb/mc_usb_handler.hpp new file mode 100644 index 0000000..e9dff8d --- /dev/null +++ b/mc_mitm/source/usb/mc_usb_handler.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020-2023 ndeadly + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::usb { + + Result Launch(); + void WaitFinished(); + +}