diff --git a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp index ab331b7bad..7c948d8ae5 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp +++ b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp @@ -14,6 +14,7 @@ #include #include +#include "Common/BitUtils.h" #include "Common/MsgHandler.h" #include "Core/Config/MainSettings.h" @@ -62,6 +63,11 @@ bool LibUSBBluetoothAdapter::IsWiiBTModule() const return m_is_wii_bt_module; } +bool LibUSBBluetoothAdapter::AreCommandsPendingResponse() const +{ + return !m_pending_hci_transfers.empty() || !m_unacknowledged_commands.empty(); +} + bool LibUSBBluetoothAdapter::HasConfiguredBluetoothDevice() { const int configured_vid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID); @@ -159,7 +165,7 @@ LibUSBBluetoothAdapter::~LibUSBBluetoothAdapter() return; // Wait for completion (or time out) of all HCI commands. - while (!m_pending_hci_transfers.empty() && !m_unacknowledged_commands.empty()) + while (AreCommandsPendingResponse()) { (void)ReceiveHCIEvent(); Common::YieldCPU(); @@ -394,6 +400,50 @@ void LibUSBBluetoothAdapter::SendControlTransfer(std::span data) ScheduleControlTransfer(REQUEST_TYPE, 0, 0, 0, data, Clock::now()); } +bool LibUSBBluetoothAdapter::SendBlockingCommand(std::span data, std::span response) +{ + SendControlTransfer(data); + + const hci_cmd_hdr_t cmd = Common::BitCastPtr(data.data()); + + while (AreCommandsPendingResponse()) + { + const auto event = ReceiveHCIEvent(); + + if (event.empty()) + { + Common::YieldCPU(); + continue; + } + + if (event[0] != HCI_EVENT_COMMAND_COMPL) + continue; + + if (event.size() < sizeof(hci_event_hdr_t) + sizeof(hci_command_compl_ep)) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Undersized hci_command_compl_ep"); + continue; + } + + const hci_command_compl_ep compl_event = + Common::BitCastPtr(event.data() + sizeof(hci_event_hdr_t)); + if (compl_event.opcode != cmd.opcode) + continue; + + if (event.size() < sizeof(hci_event_hdr_t) + sizeof(hci_command_compl_ep) + response.size()) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Undersized command result"); + break; + } + + std::copy_n(event.data() + sizeof(hci_event_hdr_t) + sizeof(hci_command_compl_ep), + response.size(), response.data()); + return true; + } + + return false; +} + void LibUSBBluetoothAdapter::StartInputTransfers() { constexpr auto callback = LibUSBMemFunCallback<&LibUSBBluetoothAdapter::HandleInputTransfer>(); diff --git a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h index c2bd693b10..d9f6aa33ef 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h +++ b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h @@ -53,6 +53,11 @@ public: // Schedule a transfer to be submitted as soon as possible. void SendControlTransfer(std::span data); + // Blocks and eats events until a command complete event of the correct opcode is received. + // Returns true on success or false on error or timeout. + // The written response does not include the event header or command complete data. + bool SendBlockingCommand(std::span data, std::span response); + bool IsWiiBTModule() const; private: @@ -122,6 +127,7 @@ private: std::deque m_unacknowledged_commands; bool IsControllerReadyForCommand() const; + bool AreCommandsPendingResponse() const; // Give the transfer to the worker and track the command appropriately. // This should only be used when IsControllerReadyForCommand is true.