mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Merge pull request #13751 from jordan-woyak/btreal-improvements
Bluetooth Passthrough Improvements
This commit is contained in:
@ -695,6 +695,8 @@ if(TARGET LibUSB::LibUSB)
|
||||
IOS/USB/LibusbDevice.h
|
||||
IOS/USB/Bluetooth/BTReal.cpp
|
||||
IOS/USB/Bluetooth/BTReal.h
|
||||
IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp
|
||||
IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -3,9 +3,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/IOS/Device.h"
|
||||
#include "Core/IOS/IOS.h"
|
||||
@ -15,6 +12,18 @@ class SysConf;
|
||||
|
||||
namespace IOS::HLE
|
||||
{
|
||||
template <typename T>
|
||||
static void DoStateForMessage(EmulationKernel& ios, PointerWrap& p, std::unique_ptr<T>& message)
|
||||
{
|
||||
u32 request_address = (message != nullptr) ? message->ios_request.address : 0;
|
||||
p.Do(request_address);
|
||||
if (request_address != 0)
|
||||
{
|
||||
IOCtlVRequest request{ios.GetSystem(), request_address};
|
||||
message = std::make_unique<T>(ios, request);
|
||||
}
|
||||
}
|
||||
|
||||
void BackUpBTInfoSection(const SysConf* sysconf);
|
||||
void RestoreBTInfoSection(SysConf* sysconf);
|
||||
|
||||
|
@ -79,18 +79,6 @@ BluetoothEmuDevice::BluetoothEmuDevice(EmulationKernel& ios, const std::string&
|
||||
|
||||
BluetoothEmuDevice::~BluetoothEmuDevice() = default;
|
||||
|
||||
template <typename T>
|
||||
static void DoStateForMessage(EmulationKernel& ios, PointerWrap& p, std::unique_ptr<T>& message)
|
||||
{
|
||||
u32 request_address = (message != nullptr) ? message->ios_request.address : 0;
|
||||
p.Do(request_address);
|
||||
if (request_address != 0)
|
||||
{
|
||||
IOCtlVRequest request{ios.GetSystem(), request_address};
|
||||
message = std::make_unique<T>(ios, request);
|
||||
}
|
||||
}
|
||||
|
||||
void BluetoothEmuDevice::DoState(PointerWrap& p)
|
||||
{
|
||||
bool passthrough_bluetooth = false;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,25 +6,22 @@
|
||||
#if defined(__LIBUSB__)
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/Timer.h"
|
||||
|
||||
#include "Core/IOS/IOS.h"
|
||||
#include "Core/IOS/USB/Bluetooth/BTBase.h"
|
||||
#include "Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h"
|
||||
#include "Core/IOS/USB/Bluetooth/hci.h"
|
||||
#include "Core/IOS/USB/USBV0.h"
|
||||
#include "Core/LibusbUtils.h"
|
||||
|
||||
class PointerWrap;
|
||||
struct libusb_device;
|
||||
struct libusb_device_handle;
|
||||
struct libusb_device_descriptor;
|
||||
struct libusb_transfer;
|
||||
|
||||
namespace IOS::HLE
|
||||
{
|
||||
@ -49,87 +46,55 @@ public:
|
||||
std::optional<IPCReply> Open(const OpenRequest& request) override;
|
||||
std::optional<IPCReply> Close(u32 fd) override;
|
||||
std::optional<IPCReply> IOCtlV(const IOCtlVRequest& request) override;
|
||||
void Update() override;
|
||||
|
||||
void DoState(PointerWrap& p) override;
|
||||
void UpdateSyncButtonState(bool is_held) override;
|
||||
void TriggerSyncButtonPressedEvent() override;
|
||||
void TriggerSyncButtonHeldEvent() override;
|
||||
|
||||
void HandleCtrlTransfer(libusb_transfer* finished_transfer);
|
||||
void HandleBulkOrIntrTransfer(libusb_transfer* finished_transfer);
|
||||
|
||||
static bool IsConfiguredBluetoothDevice(u16 vid, u16 pid);
|
||||
static bool HasConfiguredBluetoothDevice();
|
||||
|
||||
struct BluetoothDeviceInfo
|
||||
{
|
||||
u16 vid;
|
||||
u16 pid;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
static std::vector<BluetoothDeviceInfo> ListDevices();
|
||||
|
||||
private:
|
||||
static constexpr u8 INTERFACE = 0x00;
|
||||
// Arbitrarily chosen value that allows emulated software to send commands often enough
|
||||
// so that the sync button event is triggered at least every 200ms.
|
||||
// Ideally this should be equal to 0, so we don't trigger unnecessary libusb transfers.
|
||||
static constexpr u32 TIMEOUT = 200;
|
||||
using BufferType = LibUSBBluetoothAdapter::BufferType;
|
||||
|
||||
std::unique_ptr<LibUSBBluetoothAdapter> m_lib_usb_bt_adapter;
|
||||
|
||||
static constexpr u32 SYNC_BUTTON_HOLD_MS_TO_RESET = 10000;
|
||||
|
||||
std::atomic<SyncButtonState> m_sync_button_state{SyncButtonState::Unpressed};
|
||||
Common::Timer m_sync_button_held_timer;
|
||||
|
||||
std::string m_last_open_error;
|
||||
|
||||
LibusbUtils::Context m_context;
|
||||
libusb_device* m_device = nullptr;
|
||||
libusb_device_handle* m_handle = nullptr;
|
||||
|
||||
std::mutex m_transfers_mutex;
|
||||
struct PendingTransfer
|
||||
{
|
||||
PendingTransfer(std::unique_ptr<USB::TransferCommand> command_, std::unique_ptr<u8[]> buffer_)
|
||||
: command(std::move(command_)), buffer(std::move(buffer_))
|
||||
{
|
||||
}
|
||||
std::unique_ptr<USB::TransferCommand> command;
|
||||
std::unique_ptr<u8[]> buffer;
|
||||
};
|
||||
std::map<libusb_transfer*, PendingTransfer> m_current_transfers;
|
||||
|
||||
// Set when we received a command to which we need to fake a reply
|
||||
Common::Flag m_fake_read_buffer_size_reply;
|
||||
Common::Flag m_fake_vendor_command_reply;
|
||||
u16 m_fake_vendor_command_reply_opcode;
|
||||
// Enqueued when we observe a command to which we need to fake a reply.
|
||||
std::queue<std::function<void(USB::V0IntrMessage&)>> m_fake_replies;
|
||||
|
||||
// This stores the address of paired devices and associated link keys.
|
||||
// It is needed because some adapters forget all stored link keys when they are reset,
|
||||
// which breaks pairings because the Wii relies on the Bluetooth module to remember them.
|
||||
std::map<bdaddr_t, linkkey_t> m_link_keys;
|
||||
Common::Flag m_need_reset_keys;
|
||||
|
||||
// This flag is set when a libusb transfer failed (for reasons other than timing out)
|
||||
// and we showed an OSD message about it.
|
||||
Common::Flag m_showed_failed_transfer;
|
||||
// Endpoints provided by the emulated software.
|
||||
std::unique_ptr<USB::V0IntrMessage> m_hci_endpoint;
|
||||
std::unique_ptr<USB::V0BulkMessage> m_acl_endpoint;
|
||||
|
||||
bool m_is_wii_bt_module = false;
|
||||
// Used for proper Bluetooth packet timing, especially for Wii remote speaker data.
|
||||
TimePoint GetTargetTime() const;
|
||||
|
||||
void TryToFillHCIEndpoint();
|
||||
void TryToFillACLEndpoint();
|
||||
|
||||
[[nodiscard]] BufferType ProcessHCIEvent(BufferType buffer);
|
||||
|
||||
void WaitForHCICommandComplete(u16 opcode);
|
||||
void SendHCIResetCommand();
|
||||
void SendHCIDeleteLinkKeyCommand();
|
||||
bool SendHCIStoreLinkKeyCommand();
|
||||
void FakeVendorCommandReply(USB::V0IntrMessage& ctrl);
|
||||
void FakeReadBufferSizeReply(USB::V0IntrMessage& ctrl);
|
||||
void FakeSyncButtonEvent(USB::V0IntrMessage& ctrl, const u8* payload, u8 size);
|
||||
|
||||
void FakeVendorCommandReply(u16 opcode, USB::V0IntrMessage& ctrl);
|
||||
|
||||
void FakeSyncButtonEvent(USB::V0IntrMessage& ctrl, std::span<const u8> payload);
|
||||
void FakeSyncButtonPressedEvent(USB::V0IntrMessage& ctrl);
|
||||
void FakeSyncButtonHeldEvent(USB::V0IntrMessage& ctrl);
|
||||
|
||||
void LoadLinkKeys();
|
||||
void SaveLinkKeys();
|
||||
|
||||
bool OpenDevice(const libusb_device_descriptor& device_descriptor, libusb_device* device);
|
||||
};
|
||||
} // namespace IOS::HLE
|
||||
|
||||
|
596
Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp
Normal file
596
Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp
Normal file
@ -0,0 +1,596 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ranges>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/IOS/USB/Bluetooth/hci.h"
|
||||
#include "Core/IOS/USB/Host.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr std::size_t BUFFER_SIZE = 1024;
|
||||
|
||||
constexpr auto HCI_COMMAND_TIMEOUT = std::chrono::milliseconds{1000};
|
||||
|
||||
constexpr u8 REQUEST_TYPE =
|
||||
u8(u8(LIBUSB_ENDPOINT_OUT) | u8(LIBUSB_REQUEST_TYPE_CLASS)) | u8(LIBUSB_RECIPIENT_DEVICE);
|
||||
|
||||
constexpr u8 HCI_EVENT = 0x81;
|
||||
constexpr u8 ACL_DATA_IN = 0x82;
|
||||
|
||||
template <auto MemFun>
|
||||
constexpr libusb_transfer_cb_fn LibUSBMemFunCallback()
|
||||
{
|
||||
return [](libusb_transfer* tr) {
|
||||
std::invoke(MemFun, static_cast<Common::ObjectType<MemFun>*>(tr->user_data), tr);
|
||||
};
|
||||
}
|
||||
|
||||
bool IsBluetoothDevice(const libusb_device_descriptor& descriptor)
|
||||
{
|
||||
constexpr u8 SUBCLASS = 0x01;
|
||||
constexpr u8 PROTOCOL_BLUETOOTH = 0x01;
|
||||
|
||||
const bool is_bluetooth_protocol = descriptor.bDeviceClass == LIBUSB_CLASS_WIRELESS &&
|
||||
descriptor.bDeviceSubClass == SUBCLASS &&
|
||||
descriptor.bDeviceProtocol == PROTOCOL_BLUETOOTH;
|
||||
|
||||
// Some devices misreport their class, so we avoid relying solely on descriptor checks and allow
|
||||
// users to specify their own VID/PID.
|
||||
return is_bluetooth_protocol || LibUSBBluetoothAdapter::IsConfiguredBluetoothDevice(
|
||||
descriptor.idVendor, descriptor.idProduct);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool LibUSBBluetoothAdapter::IsWiiBTModule() const
|
||||
{
|
||||
return m_is_wii_bt_module;
|
||||
}
|
||||
|
||||
bool LibUSBBluetoothAdapter::HasConfiguredBluetoothDevice()
|
||||
{
|
||||
const int configured_vid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID);
|
||||
const int configured_pid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID);
|
||||
return configured_vid != -1 && configured_pid != -1;
|
||||
}
|
||||
|
||||
bool LibUSBBluetoothAdapter::IsConfiguredBluetoothDevice(u16 vid, u16 pid)
|
||||
{
|
||||
const int configured_vid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID);
|
||||
const int configured_pid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID);
|
||||
return configured_vid == vid && configured_pid == pid;
|
||||
}
|
||||
|
||||
LibUSBBluetoothAdapter::LibUSBBluetoothAdapter()
|
||||
{
|
||||
if (!m_context.IsValid())
|
||||
return;
|
||||
|
||||
m_last_open_error.clear();
|
||||
|
||||
const bool has_configured_bt = HasConfiguredBluetoothDevice();
|
||||
|
||||
const int ret = m_context.GetDeviceList([&](libusb_device* device) {
|
||||
libusb_device_descriptor device_descriptor;
|
||||
libusb_get_device_descriptor(device, &device_descriptor);
|
||||
auto [make_config_descriptor_ret, config_descriptor] =
|
||||
LibusbUtils::MakeConfigDescriptor(device);
|
||||
if (make_config_descriptor_ret != LIBUSB_SUCCESS || !config_descriptor)
|
||||
{
|
||||
ERROR_LOG_FMT(IOS_WIIMOTE, "Failed to get config descriptor for device {:04x}:{:04x}: {}",
|
||||
device_descriptor.idVendor, device_descriptor.idProduct,
|
||||
LibusbUtils::ErrorWrap(make_config_descriptor_ret));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (has_configured_bt &&
|
||||
!IsConfiguredBluetoothDevice(device_descriptor.idVendor, device_descriptor.idProduct))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsBluetoothDevice(device_descriptor) && OpenDevice(device_descriptor, device))
|
||||
{
|
||||
unsigned char manufacturer[50] = {}, product[50] = {}, serial_number[50] = {};
|
||||
const int manufacturer_ret = libusb_get_string_descriptor_ascii(
|
||||
m_handle, device_descriptor.iManufacturer, manufacturer, sizeof(manufacturer));
|
||||
if (manufacturer_ret < LIBUSB_SUCCESS)
|
||||
{
|
||||
WARN_LOG_FMT(IOS_WIIMOTE,
|
||||
"Failed to get string for manufacturer descriptor {:02x} for device "
|
||||
"{:04x}:{:04x} (rev {:x}): {}",
|
||||
device_descriptor.iManufacturer, device_descriptor.idVendor,
|
||||
device_descriptor.idProduct, device_descriptor.bcdDevice,
|
||||
LibusbUtils::ErrorWrap(manufacturer_ret));
|
||||
manufacturer[0] = '?';
|
||||
manufacturer[1] = '\0';
|
||||
}
|
||||
const int product_ret = libusb_get_string_descriptor_ascii(
|
||||
m_handle, device_descriptor.iProduct, product, sizeof(product));
|
||||
if (product_ret < LIBUSB_SUCCESS)
|
||||
{
|
||||
WARN_LOG_FMT(IOS_WIIMOTE,
|
||||
"Failed to get string for product descriptor {:02x} for device "
|
||||
"{:04x}:{:04x} (rev {:x}): {}",
|
||||
device_descriptor.iProduct, device_descriptor.idVendor,
|
||||
device_descriptor.idProduct, device_descriptor.bcdDevice,
|
||||
LibusbUtils::ErrorWrap(product_ret));
|
||||
product[0] = '?';
|
||||
product[1] = '\0';
|
||||
}
|
||||
const int serial_ret = libusb_get_string_descriptor_ascii(
|
||||
m_handle, device_descriptor.iSerialNumber, serial_number, sizeof(serial_number));
|
||||
if (serial_ret < LIBUSB_SUCCESS)
|
||||
{
|
||||
WARN_LOG_FMT(IOS_WIIMOTE,
|
||||
"Failed to get string for serial number descriptor {:02x} for device "
|
||||
"{:04x}:{:04x} (rev {:x}): {}",
|
||||
device_descriptor.iSerialNumber, device_descriptor.idVendor,
|
||||
device_descriptor.idProduct, device_descriptor.bcdDevice,
|
||||
LibusbUtils::ErrorWrap(serial_ret));
|
||||
serial_number[0] = '?';
|
||||
serial_number[1] = '\0';
|
||||
}
|
||||
NOTICE_LOG_FMT(IOS_WIIMOTE, "Using device {:04x}:{:04x} (rev {:x}) for Bluetooth: {} {} {}",
|
||||
device_descriptor.idVendor, device_descriptor.idProduct,
|
||||
device_descriptor.bcdDevice, reinterpret_cast<char*>(manufacturer),
|
||||
reinterpret_cast<char*>(product), reinterpret_cast<char*>(serial_number));
|
||||
m_is_wii_bt_module =
|
||||
device_descriptor.idVendor == 0x57e && device_descriptor.idProduct == 0x305;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
m_last_open_error =
|
||||
Common::FmtFormatT("GetDeviceList failed: {0}", LibusbUtils::ErrorWrap(ret));
|
||||
}
|
||||
|
||||
if (m_handle == nullptr)
|
||||
{
|
||||
if (m_last_open_error.empty())
|
||||
{
|
||||
CriticalAlertFmtT(
|
||||
"Could not find any usable Bluetooth USB adapter for Bluetooth Passthrough.\n\n"
|
||||
"The emulated console will now stop.");
|
||||
}
|
||||
else
|
||||
{
|
||||
CriticalAlertFmtT(
|
||||
"Could not find any usable Bluetooth USB adapter for Bluetooth Passthrough.\n"
|
||||
"The following error occurred when Dolphin tried to use an adapter:\n{0}\n\n"
|
||||
"The emulated console will now stop.",
|
||||
m_last_open_error);
|
||||
}
|
||||
Core::QueueHostJob(&Core::Stop);
|
||||
return;
|
||||
}
|
||||
|
||||
StartInputTransfers();
|
||||
|
||||
m_output_worker.Reset("Bluetooth Output",
|
||||
std::bind_front(&LibUSBBluetoothAdapter::SubmitTimedTransfer, this));
|
||||
}
|
||||
|
||||
LibUSBBluetoothAdapter::~LibUSBBluetoothAdapter()
|
||||
{
|
||||
if (m_handle == nullptr)
|
||||
return;
|
||||
|
||||
// Wait for completion (or time out) of all HCI commands.
|
||||
while (!m_pending_hci_transfers.empty() && !m_unacknowledged_commands.empty())
|
||||
{
|
||||
(void)ReceiveHCIEvent();
|
||||
Common::YieldCPU();
|
||||
}
|
||||
|
||||
m_output_worker.Shutdown();
|
||||
|
||||
// Stop the repeating input transfers.
|
||||
if (std::lock_guard lg{m_transfers_mutex}; true)
|
||||
m_run_input_transfers = false;
|
||||
|
||||
// Cancel all LibUSB transfers.
|
||||
std::ranges::for_each(m_transfer_buffers | std::ranges::views::keys, libusb_cancel_transfer);
|
||||
|
||||
// Wait for transfer callbacks and clean up all the buffers.
|
||||
while (!m_transfer_buffers.empty())
|
||||
{
|
||||
m_transfers_to_free.WaitForData();
|
||||
CleanCompletedTransfers();
|
||||
}
|
||||
|
||||
const int ret = libusb_release_interface(m_handle, 0);
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
WARN_LOG_FMT(IOS_WIIMOTE, "libusb_release_interface failed: {}", LibusbUtils::ErrorWrap(ret));
|
||||
|
||||
libusb_close(std::exchange(m_handle, nullptr));
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::Update()
|
||||
{
|
||||
// Remove timed out commands.
|
||||
const auto expired_time = Clock::now() - HCI_COMMAND_TIMEOUT;
|
||||
while (!m_unacknowledged_commands.empty() &&
|
||||
m_unacknowledged_commands.front().submit_time < expired_time)
|
||||
{
|
||||
WARN_LOG_FMT(IOS_WIIMOTE, "HCI command 0x{:04x} timed out.",
|
||||
m_unacknowledged_commands.front().opcode);
|
||||
m_unacknowledged_commands.pop_front();
|
||||
}
|
||||
|
||||
// Allow sending commands if none are pending acknowledgement.
|
||||
if (m_unacknowledged_commands.empty())
|
||||
m_num_hci_command_packets = 1;
|
||||
|
||||
// Push queued commands when the controller is ready for them.
|
||||
if (!m_pending_hci_transfers.empty() && IsControllerReadyForCommand())
|
||||
{
|
||||
DEBUG_LOG_FMT(IOS_WIIMOTE, "Submitting queued HCI command.");
|
||||
|
||||
PushHCICommand(m_pending_hci_transfers.front());
|
||||
m_pending_hci_transfers.pop();
|
||||
}
|
||||
|
||||
CleanCompletedTransfers();
|
||||
}
|
||||
|
||||
auto LibUSBBluetoothAdapter::ReceiveHCIEvent() -> BufferType
|
||||
{
|
||||
if (m_hci_event_queue.Empty())
|
||||
{
|
||||
Update();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto buffer = std::move(m_hci_event_queue.Front());
|
||||
m_hci_event_queue.Pop();
|
||||
|
||||
// We only care about "COMMAND_COMPL" and "COMMAND_STATUS" events here.
|
||||
// The controller will reply with one of those for every command.
|
||||
// We track which commands are still "in flight" to properly obey Num_HCI_Command_Packets.
|
||||
const auto event = buffer[0];
|
||||
if (event == HCI_EVENT_COMMAND_COMPL)
|
||||
{
|
||||
AcknowledgeCommand<hci_command_compl_ep>(buffer);
|
||||
}
|
||||
else if (event == HCI_EVENT_COMMAND_STATUS)
|
||||
{
|
||||
AcknowledgeCommand<hci_command_status_ep>(buffer);
|
||||
}
|
||||
|
||||
Update();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
auto LibUSBBluetoothAdapter::ReceiveACLData() -> BufferType
|
||||
{
|
||||
BufferType buffer;
|
||||
m_acl_data_queue.Pop(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool LibUSBBluetoothAdapter::IsControllerReadyForCommand() const
|
||||
{
|
||||
return m_num_hci_command_packets > m_unacknowledged_commands.size();
|
||||
}
|
||||
|
||||
template <typename EventType>
|
||||
void LibUSBBluetoothAdapter::AcknowledgeCommand(std::span<const u8> buffer)
|
||||
{
|
||||
if (buffer.size() < sizeof(hci_event_hdr_t) + sizeof(EventType)) [[unlikely]]
|
||||
{
|
||||
WARN_LOG_FMT(IOS_WIIMOTE, "Undersized HCI event");
|
||||
return;
|
||||
}
|
||||
|
||||
EventType ev;
|
||||
std::memcpy(&ev, buffer.data() + sizeof(hci_event_hdr_t), sizeof(ev));
|
||||
|
||||
const auto it =
|
||||
std::ranges::find(m_unacknowledged_commands, ev.opcode, &OutstandingCommand::opcode);
|
||||
if (it != m_unacknowledged_commands.end())
|
||||
{
|
||||
DEBUG_LOG_FMT(IOS_WIIMOTE, "HCI command acknowledged: 0x{:04x}", ev.opcode);
|
||||
m_unacknowledged_commands.erase(it);
|
||||
}
|
||||
else if (ev.opcode != 0x0000)
|
||||
{
|
||||
WARN_LOG_FMT(IOS_WIIMOTE, "Unexpected opcode acknowledgement: 0x{:04x}", ev.opcode);
|
||||
}
|
||||
|
||||
m_num_hci_command_packets = ev.num_cmd_pkts;
|
||||
}
|
||||
|
||||
libusb_transfer* LibUSBBluetoothAdapter::AllocateTransfer(std::size_t buffer_size)
|
||||
{
|
||||
auto& item =
|
||||
*m_transfer_buffers.emplace(libusb_alloc_transfer(0), Common::UniqueBuffer<u8>{buffer_size})
|
||||
.first;
|
||||
|
||||
auto* const transfer = item.first;
|
||||
transfer->buffer = item.second.get();
|
||||
|
||||
return transfer;
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::FreeTransfer(libusb_transfer* transfer)
|
||||
{
|
||||
libusb_free_transfer(transfer);
|
||||
m_transfer_buffers.erase(transfer);
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::CleanCompletedTransfers()
|
||||
{
|
||||
libusb_transfer* transfer{};
|
||||
while (m_transfers_to_free.Pop(transfer))
|
||||
FreeTransfer(transfer);
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::PushHCICommand(TimedTransfer transfer)
|
||||
{
|
||||
hci_cmd_hdr_t cmd;
|
||||
std::memcpy(&cmd, libusb_control_transfer_get_data(transfer.transfer), sizeof(cmd));
|
||||
|
||||
// Push the time forward so our timeouts work properly if command was queued and delayed.
|
||||
const auto submit_time = std::max(transfer.target_time, Clock::now());
|
||||
|
||||
m_unacknowledged_commands.emplace_back(OutstandingCommand{cmd.opcode, submit_time});
|
||||
|
||||
// This function is only invoked when this value is at least 1.
|
||||
assert(m_num_hci_command_packets >= 1);
|
||||
--m_num_hci_command_packets;
|
||||
|
||||
m_output_worker.EmplaceItem(transfer);
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::SubmitTimedTransfer(TimedTransfer transfer)
|
||||
{
|
||||
if (Config::Get(Config::MAIN_PRECISION_FRAME_TIMING))
|
||||
m_precision_timer.SleepUntil(transfer.target_time);
|
||||
else
|
||||
std::this_thread::sleep_until(transfer.target_time);
|
||||
|
||||
SubmitTransfer(transfer.transfer);
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::SubmitTransfer(libusb_transfer* transfer)
|
||||
{
|
||||
const int ret = libusb_submit_transfer(transfer);
|
||||
|
||||
if (ret == LIBUSB_SUCCESS)
|
||||
return;
|
||||
|
||||
ERROR_LOG_FMT(IOS_WIIMOTE, "libusb_submit_transfer: {}", LibusbUtils::ErrorWrap(ret));
|
||||
|
||||
// Failed transers will not invoke callbacks so we must mark the buffer to be free'd here.
|
||||
std::lock_guard lk{m_transfers_mutex};
|
||||
m_transfers_to_free.Emplace(transfer);
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::ScheduleBulkTransfer(u8 endpoint, std::span<const u8> data,
|
||||
TimePoint target_time)
|
||||
{
|
||||
constexpr auto callback = LibUSBMemFunCallback<&LibUSBBluetoothAdapter::HandleOutputTransfer>();
|
||||
|
||||
auto* const transfer = AllocateTransfer(data.size());
|
||||
libusb_fill_bulk_transfer(transfer, m_handle, endpoint, transfer->buffer, int(data.size()),
|
||||
callback, this, 0);
|
||||
|
||||
std::ranges::copy(data, transfer->buffer);
|
||||
|
||||
m_output_worker.EmplaceItem(transfer, target_time);
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::ScheduleControlTransfer(u8 type, u8 request, u8 value, u8 index,
|
||||
std::span<const u8> data,
|
||||
TimePoint target_time)
|
||||
{
|
||||
constexpr auto callback = LibUSBMemFunCallback<&LibUSBBluetoothAdapter::HandleOutputTransfer>();
|
||||
|
||||
auto* const transfer = AllocateTransfer(data.size() + LIBUSB_CONTROL_SETUP_SIZE);
|
||||
|
||||
libusb_fill_control_setup(transfer->buffer, type, request, value, index, u16(data.size()));
|
||||
libusb_fill_control_transfer(transfer, m_handle, transfer->buffer, callback, this, 0);
|
||||
|
||||
std::ranges::copy(data, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE);
|
||||
|
||||
const TimedTransfer timed_transfer{transfer, target_time};
|
||||
|
||||
if (IsControllerReadyForCommand())
|
||||
{
|
||||
PushHCICommand(timed_transfer);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG_FMT(IOS_WIIMOTE, "Queueing HCI command while controller is busy.");
|
||||
m_pending_hci_transfers.emplace(timed_transfer);
|
||||
}
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::SendControlTransfer(std::span<const u8> data)
|
||||
{
|
||||
ScheduleControlTransfer(REQUEST_TYPE, 0, 0, 0, data, Clock::now());
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::StartInputTransfers()
|
||||
{
|
||||
constexpr auto callback = LibUSBMemFunCallback<&LibUSBBluetoothAdapter::HandleInputTransfer>();
|
||||
|
||||
// Incoming HCI events.
|
||||
{
|
||||
auto* const transfer = AllocateTransfer(BUFFER_SIZE);
|
||||
libusb_fill_interrupt_transfer(transfer, m_handle, HCI_EVENT, transfer->buffer, BUFFER_SIZE,
|
||||
callback, this, 0);
|
||||
SubmitTransfer(transfer);
|
||||
}
|
||||
|
||||
// Incoming ACL data.
|
||||
{
|
||||
auto* const transfer = AllocateTransfer(BUFFER_SIZE);
|
||||
libusb_fill_bulk_transfer(transfer, m_handle, ACL_DATA_IN, transfer->buffer, BUFFER_SIZE,
|
||||
callback, this, 0);
|
||||
SubmitTransfer(transfer);
|
||||
}
|
||||
}
|
||||
|
||||
bool LibUSBBluetoothAdapter::OpenDevice(const libusb_device_descriptor& device_descriptor,
|
||||
libusb_device* device)
|
||||
{
|
||||
const int ret = libusb_open(device, &m_handle);
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
m_last_open_error = Common::FmtFormatT("Failed to open Bluetooth device {:04x}:{:04x}: {}",
|
||||
device_descriptor.idVendor, device_descriptor.idProduct,
|
||||
LibusbUtils::ErrorWrap(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detaching always fails as a regular user on FreeBSD
|
||||
// https://lists.freebsd.org/pipermail/freebsd-usb/2016-March/014161.html
|
||||
#ifndef __FreeBSD__
|
||||
int result = libusb_set_auto_detach_kernel_driver(m_handle, 1);
|
||||
if (result != LIBUSB_SUCCESS)
|
||||
{
|
||||
result = libusb_detach_kernel_driver(m_handle, INTERFACE);
|
||||
if (result != LIBUSB_SUCCESS && result != LIBUSB_ERROR_NOT_FOUND &&
|
||||
result != LIBUSB_ERROR_NOT_SUPPORTED)
|
||||
{
|
||||
m_last_open_error = Common::FmtFormatT(
|
||||
"Failed to detach kernel driver for BT passthrough: {0}", LibusbUtils::ErrorWrap(result));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (const int result2 = libusb_claim_interface(m_handle, INTERFACE); result2 != LIBUSB_SUCCESS)
|
||||
{
|
||||
m_last_open_error = Common::FmtFormatT("Failed to claim interface for BT passthrough: {0}",
|
||||
LibusbUtils::ErrorWrap(result2));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<LibUSBBluetoothAdapter::BluetoothDeviceInfo> LibUSBBluetoothAdapter::ListDevices()
|
||||
{
|
||||
std::vector<BluetoothDeviceInfo> device_list;
|
||||
LibusbUtils::Context context;
|
||||
|
||||
if (!context.IsValid())
|
||||
return {};
|
||||
|
||||
int result = context.GetDeviceList([&device_list](libusb_device* device) {
|
||||
auto [config_ret, config] = LibusbUtils::MakeConfigDescriptor(device, 0);
|
||||
if (config_ret != LIBUSB_SUCCESS)
|
||||
return true;
|
||||
|
||||
libusb_device_descriptor desc;
|
||||
if (libusb_get_device_descriptor(device, &desc) != LIBUSB_SUCCESS)
|
||||
return true;
|
||||
|
||||
if (IsBluetoothDevice(desc))
|
||||
{
|
||||
const std::string device_name =
|
||||
IOS::HLE::USBHost::GetDeviceNameFromVIDPID(desc.idVendor, desc.idProduct);
|
||||
device_list.push_back({desc.idVendor, desc.idProduct, device_name});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (result < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(IOS_USB, "Failed to get device list: {}", LibusbUtils::ErrorWrap(result));
|
||||
return device_list;
|
||||
}
|
||||
|
||||
return device_list;
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::HandleOutputTransfer(libusb_transfer* tr)
|
||||
{
|
||||
HandleTransferError(tr);
|
||||
|
||||
std::lock_guard lg{m_transfers_mutex};
|
||||
m_transfers_to_free.Emplace(tr);
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::HandleInputTransfer(libusb_transfer* tr)
|
||||
{
|
||||
if (tr->status == LIBUSB_TRANSFER_COMPLETED)
|
||||
{
|
||||
const bool is_hci_event = tr->endpoint == HCI_EVENT;
|
||||
auto& queue = is_hci_event ? m_hci_event_queue : m_acl_data_queue;
|
||||
|
||||
if (queue.Size() < MAX_INPUT_QUEUE_SIZE)
|
||||
{
|
||||
if (tr->actual_length != 0)
|
||||
{
|
||||
BufferType buffer(tr->actual_length);
|
||||
std::copy_n(tr->buffer, tr->actual_length, buffer.data());
|
||||
queue.Emplace(std::move(buffer));
|
||||
}
|
||||
|
||||
m_showed_long_queue_drop = false;
|
||||
}
|
||||
else if (!std::exchange(m_showed_long_queue_drop, true))
|
||||
{
|
||||
// This will happen when pausing the emulator.
|
||||
WARN_LOG_FMT(IOS_WIIMOTE, "{} queue is too long. Packets will be dropped.",
|
||||
(is_hci_event ? "HCI" : "ACL"));
|
||||
}
|
||||
}
|
||||
|
||||
HandleTransferError(tr);
|
||||
|
||||
std::lock_guard lg{m_transfers_mutex};
|
||||
|
||||
if (m_run_input_transfers)
|
||||
{
|
||||
// Continuously repeat this input transfer so long as we're still running.
|
||||
const auto ret = libusb_submit_transfer(tr);
|
||||
if (ret == LIBUSB_SUCCESS)
|
||||
return;
|
||||
|
||||
ERROR_LOG_FMT(IOS_WIIMOTE, "HandleInputTransfer libusb_submit_transfer: {}",
|
||||
LibusbUtils::ErrorWrap(ret));
|
||||
}
|
||||
|
||||
m_transfers_to_free.Emplace(tr);
|
||||
}
|
||||
|
||||
void LibUSBBluetoothAdapter::HandleTransferError(libusb_transfer* transfer)
|
||||
{
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED &&
|
||||
transfer->status != LIBUSB_TRANSFER_CANCELLED)
|
||||
{
|
||||
ERROR_LOG_FMT(IOS_WIIMOTE, "libusb transfer failed: {}",
|
||||
LibusbUtils::ErrorWrap(transfer->status));
|
||||
if (!std::exchange(m_showed_failed_transfer, true))
|
||||
{
|
||||
Core::DisplayMessage("Failed to send a command to the Bluetooth adapter.", 10000);
|
||||
Core::DisplayMessage("It may not be compatible with passthrough mode.", 10000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_showed_failed_transfer = false;
|
||||
}
|
||||
}
|
157
Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h
Normal file
157
Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h
Normal file
@ -0,0 +1,157 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include "Common/Buffer.h"
|
||||
#include "Common/Timer.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
|
||||
#include "Core/LibusbUtils.h"
|
||||
|
||||
struct libusb_device_handle;
|
||||
struct libusb_device_descriptor;
|
||||
struct libusb_transfer;
|
||||
|
||||
class LibUSBBluetoothAdapter
|
||||
{
|
||||
public:
|
||||
using BufferType = Common::UniqueBuffer<u8>;
|
||||
|
||||
struct BluetoothDeviceInfo
|
||||
{
|
||||
u16 vid;
|
||||
u16 pid;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
static std::vector<BluetoothDeviceInfo> ListDevices();
|
||||
static bool IsConfiguredBluetoothDevice(u16 vid, u16 pid);
|
||||
static bool HasConfiguredBluetoothDevice();
|
||||
|
||||
// Public interface is intended to be used by a single thread.
|
||||
|
||||
LibUSBBluetoothAdapter();
|
||||
~LibUSBBluetoothAdapter();
|
||||
|
||||
// Return a packet at the front of the queue, or an empty buffer when nothing is queued.
|
||||
// ReceiveHCIEvent is expected to be called regularly to do some internal processing.
|
||||
[[nodiscard]] BufferType ReceiveHCIEvent();
|
||||
[[nodiscard]] BufferType ReceiveACLData();
|
||||
|
||||
// Schedule a transfer to submit at a specific time.
|
||||
void ScheduleBulkTransfer(u8 endpoint, std::span<const u8> data, TimePoint target_time);
|
||||
void ScheduleControlTransfer(u8 type, u8 request, u8 value, u8 index, std::span<const u8> data,
|
||||
TimePoint target_time);
|
||||
|
||||
// Schedule a transfer to be submitted as soon as possible.
|
||||
void SendControlTransfer(std::span<const u8> data);
|
||||
|
||||
bool IsWiiBTModule() const;
|
||||
|
||||
private:
|
||||
// Inputs will be dropped when queue is full.
|
||||
static constexpr std::size_t MAX_INPUT_QUEUE_SIZE = 100;
|
||||
|
||||
static constexpr u8 INTERFACE = 0x00;
|
||||
|
||||
std::string m_last_open_error;
|
||||
|
||||
LibusbUtils::Context m_context;
|
||||
libusb_device_handle* m_handle = nullptr;
|
||||
|
||||
// Protects run-flag and Push'ing to the completed transfer queue.
|
||||
std::mutex m_transfers_mutex;
|
||||
|
||||
bool m_run_input_transfers = true;
|
||||
|
||||
// callback/worker threads push transfers here that are ready for clean up.
|
||||
// This avoids locking around m_transfer_buffers.
|
||||
Common::WaitableSPSCQueue<libusb_transfer*> m_transfers_to_free;
|
||||
|
||||
// Incoming packets Push'd from libusb callback thread.
|
||||
Common::SPSCQueue<BufferType> m_hci_event_queue;
|
||||
Common::SPSCQueue<BufferType> m_acl_data_queue;
|
||||
|
||||
// All transfers are alloc'd and kept here until complete.
|
||||
std::map<libusb_transfer*, Common::UniqueBuffer<u8>> m_transfer_buffers;
|
||||
|
||||
struct TimedTransfer
|
||||
{
|
||||
libusb_transfer* transfer{};
|
||||
TimePoint target_time{};
|
||||
};
|
||||
|
||||
// Outgoing data is submitted on a worker thread.
|
||||
// This allows proper packet timing without throttling the core thread.
|
||||
Common::WorkQueueThreadSP<TimedTransfer> m_output_worker;
|
||||
|
||||
// Used by the output worker.
|
||||
Common::PrecisionTimer m_precision_timer;
|
||||
|
||||
// Set to reduce OSD and log spam.
|
||||
bool m_showed_failed_transfer = false;
|
||||
bool m_showed_long_queue_drop = false;
|
||||
|
||||
// Some responses need to be fabricated if we aren't using the Nintendo BT module.
|
||||
bool m_is_wii_bt_module = false;
|
||||
|
||||
// Bluetooth spec's Num_HCI_Command_Packets.
|
||||
// This is the number of hci commands that the host is allowed to send.
|
||||
// We track this to send commands only when the controller is ready.
|
||||
u8 m_num_hci_command_packets = 1;
|
||||
|
||||
// HCI commands are queued when the controller isn't ready for them.
|
||||
// They will be sent after COMMAND_COMPL or COMMAND_STATUS signal readiness.
|
||||
std::queue<TimedTransfer> m_pending_hci_transfers;
|
||||
|
||||
struct OutstandingCommand
|
||||
{
|
||||
u16 opcode{};
|
||||
TimePoint submit_time{};
|
||||
};
|
||||
|
||||
// Sent HCI commands that have yet to receive a COMMAND_COMPL or COMMAND_STATUS response.
|
||||
// Container size will be small, around 2.
|
||||
std::deque<OutstandingCommand> m_unacknowledged_commands;
|
||||
|
||||
bool IsControllerReadyForCommand() const;
|
||||
|
||||
// Give the transfer to the worker and track the command appropriately.
|
||||
// This should only be used when IsControllerReadyForCommand is true.
|
||||
void PushHCICommand(TimedTransfer);
|
||||
|
||||
template <typename EventType>
|
||||
void AcknowledgeCommand(std::span<const u8> buffer);
|
||||
|
||||
libusb_transfer* AllocateTransfer(std::size_t buffer_size);
|
||||
void FreeTransfer(libusb_transfer*);
|
||||
void CleanCompletedTransfers();
|
||||
|
||||
// Do some HCI logic and clean up completed transfers.
|
||||
void Update();
|
||||
|
||||
// Sleep until the target time and then submit the transfer.
|
||||
// Used by worker thread.
|
||||
void SubmitTimedTransfer(TimedTransfer);
|
||||
|
||||
// Immediately submit transfer with libusb.
|
||||
void SubmitTransfer(libusb_transfer*);
|
||||
|
||||
// Start repetitive transfers to fill the hci-event and acl-data queues.
|
||||
void StartInputTransfers();
|
||||
|
||||
// LibUSB callbacks. Invoked from the LibUSB context thread.
|
||||
void HandleOutputTransfer(libusb_transfer*);
|
||||
void HandleInputTransfer(libusb_transfer*);
|
||||
|
||||
// Error logging.
|
||||
void HandleTransferError(libusb_transfer*);
|
||||
|
||||
bool OpenDevice(const libusb_device_descriptor& device_descriptor, libusb_device* device);
|
||||
};
|
@ -95,7 +95,7 @@ static size_t s_state_writes_in_queue;
|
||||
static std::condition_variable s_state_write_queue_is_empty;
|
||||
|
||||
// Don't forget to increase this after doing changes on the savestate system
|
||||
constexpr u32 STATE_VERSION = 174; // Last changed in PR 13342
|
||||
constexpr u32 STATE_VERSION = 175; // Last changed in PR 13751
|
||||
|
||||
// Increase this if the StateExtendedHeader definition changes
|
||||
constexpr u32 EXTENDED_HEADER_VERSION = 1; // Last changed in PR 12217
|
||||
|
@ -402,6 +402,7 @@
|
||||
<ClInclude Include="Core\IOS\USB\Bluetooth\BTEmu.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Bluetooth\BTReal.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Bluetooth\BTStub.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Bluetooth\LibUSBBluetoothAdapter.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Bluetooth\hci.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Bluetooth\l2cap.h" />
|
||||
<ClInclude Include="Core\IOS\USB\Bluetooth\WiimoteDevice.h" />
|
||||
@ -1079,6 +1080,7 @@
|
||||
<ClCompile Include="Core\IOS\USB\Bluetooth\BTEmu.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Bluetooth\BTReal.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Bluetooth\BTStub.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Bluetooth\LibUSBBluetoothAdapter.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Bluetooth\WiimoteDevice.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Bluetooth\WiimoteHIDAttr.cpp" />
|
||||
<ClCompile Include="Core\IOS\USB\Common.cpp" />
|
||||
|
@ -15,9 +15,6 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QVariant>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
#include "Common/Config/Config.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
|
||||
@ -28,7 +25,7 @@
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
#include "Core/IOS/IOS.h"
|
||||
#include "Core/IOS/USB/Bluetooth/BTReal.h"
|
||||
#include "Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h"
|
||||
#include "Core/NetPlayProto.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/WiiUtils.h"
|
||||
@ -40,8 +37,6 @@
|
||||
#include "DolphinQt/QtUtils/SignalBlocking.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
#include "UICommon/UICommon.h"
|
||||
|
||||
WiimoteControllersWidget::WiimoteControllersWidget(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
CreateLayout();
|
||||
@ -80,7 +75,7 @@ void WiimoteControllersWidget::StartBluetoothAdapterRefresh()
|
||||
|
||||
const auto scan_func = [this]() {
|
||||
INFO_LOG_FMT(COMMON, "Refreshing Bluetooth adapter list...");
|
||||
auto device_list = IOS::HLE::BluetoothRealDevice::ListDevices();
|
||||
auto device_list = LibUSBBluetoothAdapter::ListDevices();
|
||||
INFO_LOG_FMT(COMMON, "{} Bluetooth adapters available.", device_list.size());
|
||||
const auto refresh_complete_func = [this, devices = std::move(device_list)]() {
|
||||
OnBluetoothAdapterRefreshComplete(devices);
|
||||
@ -92,7 +87,7 @@ void WiimoteControllersWidget::StartBluetoothAdapterRefresh()
|
||||
}
|
||||
|
||||
void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete(
|
||||
const std::vector<IOS::HLE::BluetoothRealDevice::BluetoothDeviceInfo>& devices)
|
||||
const std::vector<LibUSBBluetoothAdapter::BluetoothDeviceInfo>& devices)
|
||||
{
|
||||
const int configured_vid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID);
|
||||
const int configured_pid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID);
|
||||
@ -114,7 +109,7 @@ void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete(
|
||||
m_bluetooth_adapters->addItem(device_info, QVariant::fromValue(device));
|
||||
|
||||
if (!found_configured_device &&
|
||||
IOS::HLE::BluetoothRealDevice::IsConfiguredBluetoothDevice(device.vid, device.pid))
|
||||
LibUSBBluetoothAdapter::IsConfiguredBluetoothDevice(device.vid, device.pid))
|
||||
{
|
||||
found_configured_device = true;
|
||||
m_bluetooth_adapters->setCurrentIndex(m_bluetooth_adapters->count() - 1);
|
||||
@ -126,7 +121,7 @@ void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete(
|
||||
const QString name = QLatin1Char{'['} + tr("disconnected") + QLatin1Char(']');
|
||||
const std::string name_str = name.toStdString();
|
||||
|
||||
IOS::HLE::BluetoothRealDevice::BluetoothDeviceInfo disconnected_device;
|
||||
LibUSBBluetoothAdapter::BluetoothDeviceInfo disconnected_device;
|
||||
disconnected_device.vid = configured_vid;
|
||||
disconnected_device.pid = configured_pid;
|
||||
disconnected_device.name = name_str;
|
||||
@ -314,8 +309,8 @@ void WiimoteControllersWidget::OnBluetoothPassthroughDeviceChanged(int index)
|
||||
return;
|
||||
}
|
||||
|
||||
auto device_info = m_bluetooth_adapters->itemData(index)
|
||||
.value<IOS::HLE::BluetoothRealDevice::BluetoothDeviceInfo>();
|
||||
auto device_info =
|
||||
m_bluetooth_adapters->itemData(index).value<LibUSBBluetoothAdapter::BluetoothDeviceInfo>();
|
||||
|
||||
Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID, device_info.pid);
|
||||
Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID, device_info.vid);
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <QWidget>
|
||||
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "Core/IOS/USB/Bluetooth/BTReal.h"
|
||||
#include "Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h"
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
@ -39,7 +39,7 @@ private:
|
||||
void OnBluetoothPassthroughSyncPressed();
|
||||
void OnBluetoothPassthroughResetPressed();
|
||||
void OnBluetoothAdapterRefreshComplete(
|
||||
const std::vector<IOS::HLE::BluetoothRealDevice::BluetoothDeviceInfo>& devices);
|
||||
const std::vector<LibUSBBluetoothAdapter::BluetoothDeviceInfo>& devices);
|
||||
void OnWiimoteRefreshPressed();
|
||||
void OnWiimoteConfigure(size_t index);
|
||||
void StartBluetoothAdapterRefresh();
|
||||
|
Reference in New Issue
Block a user