Merge pull request #13791 from jordan-woyak/realtek-firmware-loader

BTReal: Implement Realtek firmware loading.
This commit is contained in:
JMC47
2025-08-04 19:42:59 -04:00
committed by GitHub
11 changed files with 702 additions and 31 deletions
+18 -1
View File
@@ -7,11 +7,13 @@
#include <bit>
#include <climits>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <initializer_list>
#include <span>
#include <type_traits>
#include "Common/CommonTypes.h"
namespace Common
{
///
@@ -240,4 +242,19 @@ T ExpandValue(T value, size_t left_shift_amount)
return (value << left_shift_amount) |
(T(-ExtractBit<0>(value)) >> (BitSize<T>() - left_shift_amount));
}
template <typename T>
requires(std::is_trivially_copyable_v<T>)
constexpr auto AsU8Span(const T& obj)
{
return std::span{reinterpret_cast<const u8*>(std::addressof(obj)), sizeof(obj)};
}
template <typename T>
requires(std::is_trivially_copyable_v<T>)
constexpr auto AsWritableU8Span(T& obj)
{
return std::span{reinterpret_cast<u8*>(std::addressof(obj)), sizeof(obj)};
}
} // namespace Common
+1
View File
@@ -87,6 +87,7 @@
#define RESOURCEPACK_DIR "ResourcePacks"
#define DYNAMICINPUT_DIR "DynamicInputTextures"
#define GRAPHICSMOD_DIR "GraphicMods"
#define FIRMWARE_DIR "Firmware"
#define WIISDSYNC_DIR "WiiSDSync"
#define ASSEMBLY_DIR "SavedAssembly"
#define WIIBANNERS_DIR "WiiBanners"
+1
View File
@@ -882,6 +882,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
s_user_paths[D_DYNAMICINPUT_IDX] = s_user_paths[D_LOAD_IDX] + DYNAMICINPUT_DIR DIR_SEP;
s_user_paths[D_GRAPHICSMOD_IDX] = s_user_paths[D_LOAD_IDX] + GRAPHICSMOD_DIR DIR_SEP;
s_user_paths[D_BANNERS_WIIROOT_IDX] = s_user_paths[D_LOAD_IDX] + WIIBANNERS_DIR DIR_SEP;
s_user_paths[D_FIRMWARE_IDX] = s_user_paths[D_LOAD_IDX] + FIRMWARE_DIR DIR_SEP;
s_user_paths[D_WIISDCARDSYNCFOLDER_IDX] = s_user_paths[D_LOAD_IDX] + WIISDSYNC_DIR DIR_SEP;
s_user_paths[F_DOLPHINCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + DOLPHIN_CONFIG;
s_user_paths[F_GCPADCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + GCPAD_CONFIG;
+1
View File
@@ -68,6 +68,7 @@ enum
D_RESOURCEPACK_IDX,
D_DYNAMICINPUT_IDX,
D_GRAPHICSMOD_IDX,
D_FIRMWARE_IDX,
D_GBAUSER_IDX,
D_GBASAVES_IDX,
D_WIISDCARDSYNCFOLDER_IDX,
+2
View File
@@ -699,6 +699,8 @@ if(TARGET LibUSB::LibUSB)
IOS/USB/Bluetooth/BTReal.h
IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp
IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h
IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp
IOS/USB/Bluetooth/RealtekFirmwareLoader.h
)
endif()
+5 -22
View File
@@ -13,6 +13,7 @@
#include <fmt/format.h>
#include "Common/BitUtils.h"
#include "Common/ChunkFile.h"
#include "Common/Network.h"
#include "Common/StringUtil.h"
@@ -27,24 +28,6 @@
#include "VideoCommon/OnScreenDisplay.h"
namespace
{
template <u16 Opcode, typename CommandType>
struct HCICommandPayload
{
hci_cmd_hdr_t header{Opcode, sizeof(CommandType)};
CommandType command{};
};
template <typename T>
requires(std::is_trivially_copyable_v<T>)
constexpr auto AsU8Span(const T& obj)
{
return std::span{reinterpret_cast<const u8*>(std::addressof(obj)), sizeof(obj)};
}
} // namespace
namespace IOS::HLE
{
@@ -295,7 +278,7 @@ auto BluetoothRealDevice::ProcessHCIEvent(BufferType buffer) -> BufferType
payload.command.latency = 10000;
payload.command.delay_variation = 0xffffffff;
m_lib_usb_bt_adapter->SendControlTransfer(AsU8Span(payload));
m_lib_usb_bt_adapter->SendControlTransfer(Common::AsU8Span(payload));
}
else if (event == HCI_EVENT_QOS_SETUP_COMPL)
{
@@ -402,7 +385,7 @@ void BluetoothRealDevice::TriggerSyncButtonHeldEvent()
void BluetoothRealDevice::SendHCIResetCommand()
{
INFO_LOG_FMT(IOS_WIIMOTE, "SendHCIResetCommand");
m_lib_usb_bt_adapter->SendControlTransfer(AsU8Span(hci_cmd_hdr_t{HCI_CMD_RESET, 0}));
m_lib_usb_bt_adapter->SendControlTransfer(Common::AsU8Span(hci_cmd_hdr_t{HCI_CMD_RESET, 0}));
}
void BluetoothRealDevice::SendHCIDeleteLinkKeyCommand()
@@ -413,7 +396,7 @@ void BluetoothRealDevice::SendHCIDeleteLinkKeyCommand()
payload.command.bdaddr = {};
payload.command.delete_all = 0x01;
m_lib_usb_bt_adapter->SendControlTransfer(AsU8Span(payload));
m_lib_usb_bt_adapter->SendControlTransfer(Common::AsU8Span(payload));
}
bool BluetoothRealDevice::SendHCIStoreLinkKeyCommand()
@@ -450,7 +433,7 @@ bool BluetoothRealDevice::SendHCIStoreLinkKeyCommand()
for (auto& [bdaddr, linkkey] : m_link_keys | std::views::take(num_link_keys))
payload.link_keys[index++] = {bdaddr, linkkey};
m_lib_usb_bt_adapter->SendControlTransfer(AsU8Span(payload).first(payload_size));
m_lib_usb_bt_adapter->SendControlTransfer(Common::AsU8Span(payload).first(payload_size));
return true;
}
@@ -14,10 +14,12 @@
#include <fmt/format.h>
#include <libusb.h>
#include "Common/BitUtils.h"
#include "Common/MsgHandler.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.h"
#include "Core/IOS/USB/Bluetooth/hci.h"
namespace
@@ -53,13 +55,20 @@ bool LibUSBBluetoothAdapter::IsBluetoothDevice(const libusb_device_descriptor& d
// 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);
return is_bluetooth_protocol ||
LibUSBBluetoothAdapter::IsConfiguredBluetoothDevice(descriptor.idVendor,
descriptor.idProduct) ||
IsKnownRealtekBluetoothDevice(descriptor.idVendor, descriptor.idProduct);
}
bool LibUSBBluetoothAdapter::IsWiiBTModule() const
{
return m_is_wii_bt_module;
return m_device_vid == 0x57e && m_device_pid == 0x305;
}
bool LibUSBBluetoothAdapter::AreCommandsPendingResponse() const
{
return !m_pending_hci_transfers.empty() || !m_unacknowledged_commands.empty();
}
bool LibUSBBluetoothAdapter::HasConfiguredBluetoothDevice()
@@ -115,8 +124,9 @@ LibUSBBluetoothAdapter::LibUSBBluetoothAdapter()
NOTICE_LOG_FMT(IOS_WIIMOTE, "Using device {:04x}:{:04x} (rev {:x}) for Bluetooth: {} {} {}",
device_descriptor.idVendor, device_descriptor.idProduct,
device_descriptor.bcdDevice, manufacturer, product, serial_number);
m_is_wii_bt_module =
device_descriptor.idVendor == 0x57e && device_descriptor.idProduct == 0x305;
m_device_vid = device_descriptor.idVendor;
m_device_pid = device_descriptor.idProduct;
return false;
}
return true;
@@ -151,6 +161,17 @@ LibUSBBluetoothAdapter::LibUSBBluetoothAdapter()
m_output_worker.Reset("Bluetooth Output",
std::bind_front(&LibUSBBluetoothAdapter::SubmitTimedTransfer, this));
if (IsRealtekVID(m_device_vid) || IsKnownRealtekBluetoothDevice(m_device_vid, m_device_pid))
{
INFO_LOG_FMT(IOS_WIIMOTE, "Initializing Realtek Bluetooth device: {:04x}:{:04x}", m_device_vid,
m_device_pid);
if (!InitializeRealtekBluetoothDevice(*this))
{
PanicAlertFmtT("Failed to initialize Realtek Bluetooth device.\n\n"
"Bluetooth passthrough will probably not work.");
}
}
}
LibUSBBluetoothAdapter::~LibUSBBluetoothAdapter()
@@ -159,7 +180,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 +415,50 @@ void LibUSBBluetoothAdapter::SendControlTransfer(std::span<const u8> data)
ScheduleControlTransfer(REQUEST_TYPE, 0, 0, 0, data, Clock::now());
}
bool LibUSBBluetoothAdapter::SendBlockingCommand(std::span<const u8> data, std::span<u8> response)
{
SendControlTransfer(data);
const hci_cmd_hdr_t cmd = Common::BitCastPtr<hci_cmd_hdr_t>(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<hci_command_compl_ep>(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>();
@@ -12,12 +12,20 @@
#include "Common/Timer.h"
#include "Common/WorkQueueThread.h"
#include "Core/IOS/USB/Bluetooth/hci.h"
#include "Core/LibusbUtils.h"
struct libusb_device_handle;
struct libusb_device_descriptor;
struct libusb_transfer;
template <u16 Opcode, typename CommandType>
struct HCICommandPayload
{
hci_cmd_hdr_t header{Opcode, sizeof(CommandType)};
CommandType command{};
};
class LibUSBBluetoothAdapter
{
public:
@@ -45,6 +53,11 @@ public:
// Schedule a transfer to be submitted as soon as possible.
void SendControlTransfer(std::span<const u8> 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<const u8> data, std::span<u8> response);
bool IsWiiBTModule() const;
private:
@@ -91,8 +104,8 @@ private:
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;
u16 m_device_vid = 0;
u16 m_device_pid = 0;
// Bluetooth spec's Num_HCI_Command_Packets.
// This is the number of hci commands that the host is allowed to send.
@@ -114,6 +127,7 @@ private:
std::deque<OutstandingCommand> 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.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,14 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Common/CommonTypes.h"
class LibUSBBluetoothAdapter;
bool IsRealtekVID(u16 vid);
bool IsKnownRealtekBluetoothDevice(u16 vid, u16 pid);
// Identifies the device and loads firmware as needed.
bool InitializeRealtekBluetoothDevice(LibUSBBluetoothAdapter&);
+2
View File
@@ -404,6 +404,7 @@
<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\RealtekFirmwareLoader.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" />
@@ -1083,6 +1084,7 @@
<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\RealtekFirmwareLoader.cpp" />
<ClCompile Include="Core\IOS\USB\Bluetooth\WiimoteDevice.cpp" />
<ClCompile Include="Core\IOS\USB\Bluetooth\WiimoteHIDAttr.cpp" />
<ClCompile Include="Core\IOS\USB\Common.cpp" />