diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 45d60aa435..16cc22de19 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -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" diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index c97218c37e..32f1bbc722 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -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; diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 00d20b29b2..134dbda9f9 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -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, diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 64be002800..6e7ff1a0bc 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -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() diff --git a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp index 7c948d8ae5..5c0d147792 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp +++ b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp @@ -19,6 +19,7 @@ #include "Core/Config/MainSettings.h" #include "Core/Core.h" +#include "Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.h" #include "Core/IOS/USB/Bluetooth/hci.h" namespace @@ -54,13 +55,15 @@ 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 @@ -121,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; @@ -157,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() diff --git a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h index d9f6aa33ef..0a8f63bcc1 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h +++ b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h @@ -104,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. diff --git a/Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp b/Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp new file mode 100644 index 0000000000..97527393ed --- /dev/null +++ b/Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp @@ -0,0 +1,497 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// Logic and structures are largely taken from Linux source: +// drivers/bluetooth/btusb.c +// drivers/bluetooth/btrtl.c +// drivers/bluetooth/btrtl.h + +#include "Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.h" + +#include + +#include + +#include "Common/BitUtils.h" +#include "Common/FileUtil.h" +#include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" + +#include "Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h" + +namespace +{ +constexpr u16 RTL_ROM_LMP_8723A = 0x1200; +constexpr u16 RTL_ROM_LMP_8723B = 0x8723; +constexpr u16 RTL_ROM_LMP_8821A = 0x8821; +constexpr u16 RTL_ROM_LMP_8761A = 0x8761; +constexpr u16 RTL_ROM_LMP_8822B = 0x8822; +constexpr u16 RTL_ROM_LMP_8852A = 0x8852; +constexpr u16 RTL_ROM_LMP_8851B = 0x8851; +constexpr u16 RTL_ROM_LMP_8922A = 0x8922; + +constexpr std::string_view RTL_EPATCH_SIGNATURE = "Realtech"; +constexpr std::string_view RTL_EPATCH_SIGNATURE_V2 = "RTBTCore"; + +#pragma pack(push, 1) +struct rtl_vendor_cmd +{ + std::array data; +}; +static_assert(sizeof(rtl_vendor_cmd) == 5); +struct rtl_epatch_header +{ + std::array signature; + u32 fw_version; + u16 num_patches; +}; +static_assert(sizeof(rtl_epatch_header) == 8 + 4 + 2); +struct rtl_download_cmd +{ + static constexpr int DATA_LEN = 252; + + u8 index; + std::array data; +}; +static_assert(sizeof(rtl_download_cmd) == 253); +struct rtl_download_response +{ + u8 status; + u8 index; +}; +static_assert(sizeof(rtl_download_response) == 2); +struct rtl_rom_version_evt +{ + u8 status; + u8 version; +}; +static_assert(sizeof(rtl_rom_version_evt) == 2); +#pragma pack(pop) +} // namespace + +bool IsRealtekVID(u16 vid) +{ + return vid == 0x0bda; +} + +bool IsKnownRealtekBluetoothDevice(u16 vid, u16 pid) +{ + // This list comes from Linux source: drivers/bluetooth/btusb.c + constexpr std::pair realtek_ids[] = { + // Realtek 8821CE Bluetooth devices + {0x13d3, 0x3529}, + // Realtek 8822CE Bluetooth devices + {0x0bda, 0xb00c}, + {0x0bda, 0xc822}, + // Realtek 8822CU Bluetooth devices + {0x13d3, 0x3549}, + // Realtek 8852AE Bluetooth devices + {0x0bda, 0x2852}, + {0x0bda, 0xc852}, + {0x0bda, 0x385a}, + {0x0bda, 0x4852}, + {0x04c5, 0x165c}, + {0x04ca, 0x4006}, + {0x0cb8, 0xc549}, + // Realtek 8852CE Bluetooth devices + {0x04ca, 0x4007}, + {0x04c5, 0x1675}, + {0x0cb8, 0xc558}, + {0x13d3, 0x3587}, + {0x13d3, 0x3586}, + {0x13d3, 0x3592}, + {0x0489, 0xe122}, + // Realtek 8852BE Bluetooth devices + {0x0cb8, 0xc559}, + {0x0bda, 0x4853}, + {0x0bda, 0x887b}, + {0x0bda, 0xb85b}, + {0x13d3, 0x3570}, + {0x13d3, 0x3571}, + {0x13d3, 0x3572}, + {0x13d3, 0x3591}, + {0x0489, 0xe125}, + // Realtek 8852BT/8852BE-VT Bluetooth devices + {0x0bda, 0x8520}, + // Realtek 8922AE Bluetooth devices + {0x0bda, 0x8922}, + {0x13d3, 0x3617}, + {0x13d3, 0x3616}, + {0x0489, 0xe130}, + // Additional Realtek 8723AE Bluetooth devices + {0x0930, 0x021d}, + {0x13d3, 0x3394}, + // Additional Realtek 8723BE Bluetooth devices + {0x0489, 0xe085}, + {0x0489, 0xe08b}, + {0x04f2, 0xb49f}, + {0x13d3, 0x3410}, + {0x13d3, 0x3416}, + {0x13d3, 0x3459}, + {0x13d3, 0x3494}, + // Additional Realtek 8723BU Bluetooth devices + {0x7392, 0xa611}, + // Additional Realtek 8723DE Bluetooth devices + {0x0bda, 0xb009}, + {0x2ff8, 0xb011}, + // Additional Realtek 8761BUV Bluetooth devices + {0x2357, 0x0604}, + {0x0b05, 0x190e}, + {0x2550, 0x8761}, + {0x0bda, 0x8771}, + {0x6655, 0x8771}, + {0x7392, 0xc611}, + {0x2b89, 0x8761}, + // Additional Realtek 8821AE Bluetooth devices + {0x0b05, 0x17dc}, + {0x13d3, 0x3414}, + {0x13d3, 0x3458}, + {0x13d3, 0x3461}, + {0x13d3, 0x3462}, + // Additional Realtek 8822BE Bluetooth devices + {0x13d3, 0x3526}, + {0x0b05, 0x185c}, + // Additional Realtek 8822CE Bluetooth devices + {0x04ca, 0x4005}, + {0x04c5, 0x161f}, + {0x0b05, 0x18ef}, + {0x13d3, 0x3548}, + {0x13d3, 0x3549}, + {0x13d3, 0x3553}, + {0x13d3, 0x3555}, + {0x2ff8, 0x3051}, + {0x1358, 0xc123}, + {0x0bda, 0xc123}, + {0x0cb5, 0xc547}, + }; + + return std::ranges::find(realtek_ids, std::pair(vid, pid)) != std::end(realtek_ids); +} + +// Returns the data to be loaded, or an empty buffer on error. +static Common::UniqueBuffer ParseFirmware(std::span fw_data, + std::span cfg_data, + const hci_read_local_ver_rp& local_ver, + u8 rom_version) +{ + if (fw_data.size() < sizeof(rtl_epatch_header)) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Undersized firmware file"); + return {}; + } + + const rtl_epatch_header epatch_info = Common::BitCastPtr(fw_data.data()); + if (std::memcmp(epatch_info.signature.data(), RTL_EPATCH_SIGNATURE.data(), + epatch_info.signature.size()) != 0) + { + if (std::memcmp(epatch_info.signature.data(), RTL_EPATCH_SIGNATURE_V2.data(), + epatch_info.signature.size()) == 0) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "EPATCH v2 not implemented. Please report device to developers."); + } + else + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Bad EPATCH signature"); + } + return {}; + } + + const auto fw_version = epatch_info.fw_version; + const auto num_patches = epatch_info.num_patches; + + INFO_LOG_FMT(IOS_WIIMOTE, "fw_version={} num_patches={}", epatch_info.fw_version, num_patches); + + static constexpr u8 EXTENSION_SIGNATURE[] = {0x51, 0x04, 0xfd, 0x77}; + const auto extension = fw_data.last(std::size(EXTENSION_SIGNATURE)); + if (std::memcmp(extension.data(), EXTENSION_SIGNATURE, extension.size()) != 0) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Bad extension section signature"); + return {}; + } + + // Look for the project ID starting from the end of the file. + int project_id = -1; + for (auto iter = extension.begin(); iter >= fw_data.begin() + (sizeof(rtl_epatch_header) + 3);) + { + const u8 opcode = *--iter; + const u8 length = *--iter; + const u8 data = *--iter; + + // EOF + if (opcode == 0xff) + break; + + if (length == 0) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Found instruction with length 0"); + return {}; + } + + if (opcode == 0 && length == 1) + { + project_id = data; + break; + } + + iter -= length; + } + + struct LMPSubverProjectID + { + u16 lmp_subver; + u8 project_id; + }; + + constexpr LMPSubverProjectID subver_project_ids[] = { + {RTL_ROM_LMP_8723B, 1}, {RTL_ROM_LMP_8821A, 2}, {RTL_ROM_LMP_8761A, 3}, + {RTL_ROM_LMP_8822B, 8}, {RTL_ROM_LMP_8723B, 9}, {RTL_ROM_LMP_8821A, 10}, + {RTL_ROM_LMP_8822B, 13}, {RTL_ROM_LMP_8761A, 14}, {RTL_ROM_LMP_8852A, 18}, + {RTL_ROM_LMP_8852A, 20}, {RTL_ROM_LMP_8852A, 25}, {RTL_ROM_LMP_8851B, 36}, + {RTL_ROM_LMP_8922A, 44}, {RTL_ROM_LMP_8852A, 47}, + }; + + // Verify that the project ID is what we expect for our LMP subversion. + const auto* const it = + std::ranges::find(subver_project_ids, project_id, &LMPSubverProjectID::project_id); + if (it == std::end(subver_project_ids) || it->lmp_subver != local_ver.lmp_subversion) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Unexpected project_id: {}", project_id); + return {}; + } + + // Structure is: + // u16 chip_ids[num_patches]; u16 patch_lengths[num_patches]; u32 patch_offsets[num_patches]; + if (fw_data.size() < + sizeof(rtl_epatch_header) + num_patches * (sizeof(u16) + sizeof(u16) + sizeof(u32))) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Undersized firmware file"); + return {}; + } + const auto* chip_id_base = fw_data.data() + sizeof(rtl_epatch_header); + const auto* const patch_length_base = chip_id_base + (sizeof(u16) * num_patches); + const auto* const patch_offset_base = patch_length_base + (sizeof(u16) * num_patches); + for (int i = 0; i != num_patches; ++i) + { + const u16 chip_id = Common::BitCastPtr(chip_id_base + (i * sizeof(u16))); + if (chip_id == rom_version + 1) + { + const u16 patch_length = Common::BitCastPtr(patch_length_base + (i * sizeof(u16))); + const u32 patch_offset = Common::BitCastPtr(patch_offset_base + (i * sizeof(u32))); + + INFO_LOG_FMT(IOS_WIIMOTE, "Patch length:{} offset:{}", patch_length, patch_offset); + + if (patch_offset + patch_length > fw_data.size()) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Undersized firmware file"); + return {}; + } + + auto result = Common::UniqueBuffer{patch_length + cfg_data.size()}; + std::copy_n(fw_data.data() + patch_offset, patch_length - sizeof(fw_version), result.data()); + + // The last 4 bytes are replaced with fw_version. + std::memcpy(result.data() + patch_length - sizeof(fw_version), &fw_version, + sizeof(fw_version)); + + // Append the config binary. + std::ranges::copy(cfg_data, result.data() + patch_length); + + return result; + } + } + + ERROR_LOG_FMT(IOS_WIIMOTE, "Patch not found"); + return {}; +} + +// Loads the provided data on the adapter. +static bool LoadFirmware(LibUSBBluetoothAdapter& adapter, std::span data) +{ + if (data.empty()) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Firmware data is empty"); + return false; + } + + INFO_LOG_FMT(IOS_WIIMOTE, "Loading firmware, total size: {}", data.size()); + + HCICommandPayload<0xfc20, rtl_download_cmd> payload{}; + + const auto write_block = [&](std::span data) { + rtl_download_response result{}; + if (!adapter.SendBlockingCommand(data, Common::AsWritableU8Span(result))) + return false; + + if (result.status != 0x00) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Bad load firmware status: 0x{:02x}", result.status); + return false; + } + + return true; + }; + + // Write all blocks except the last one. + auto block_iter = data.begin(); + while (data.end() - block_iter > rtl_download_cmd::DATA_LEN) + { + const auto block_end = block_iter + rtl_download_cmd::DATA_LEN; + std::copy(block_iter, block_end, payload.command.data.data()); + + if (!write_block(Common::AsU8Span(payload))) + return false; + + if (++payload.command.index == 0x80) + payload.command.index = 1; + + block_iter = block_end; + } + + // Write the last block. + const auto last_block_size = data.end() - block_iter; + const auto bytes_short_of_full_block = rtl_download_cmd::DATA_LEN - last_block_size; + + // Mark data end. + payload.command.index |= 0x80u; + payload.header.length -= bytes_short_of_full_block; + std::copy(block_iter, data.end(), payload.command.data.data()); + + if (!write_block(Common::AsU8Span(payload).first(sizeof(payload) - bytes_short_of_full_block))) + return false; + + NOTICE_LOG_FMT(IOS_WIIMOTE, "Loading firmware complete"); + return true; +} + +static constexpr u64 LocalVerUID(u16 lmp_subversion, u16 hci_revision, u8 hci_version) +{ + return u64(lmp_subversion) | (u64(hci_revision) << 16u) | (u64(hci_version) << 32u); +} + +// Returns the firmware name for a given local version. +static std::string GetFirmwareName(const hci_read_local_ver_rp& local_ver) +{ + // This list comes from Linux source: drivers/bluetooth/btrtl.c + switch (LocalVerUID(local_ver.lmp_subversion, local_ver.hci_revision, local_ver.hci_version)) + { + case LocalVerUID(RTL_ROM_LMP_8723A, 0xb, 0x6): + return "8723a"; + case LocalVerUID(RTL_ROM_LMP_8723B, 0xb, 0x6): + return "8723b"; + case LocalVerUID(RTL_ROM_LMP_8723B, 0xd, 0x8): + return "8723d"; + case LocalVerUID(RTL_ROM_LMP_8761A, 0xa, 0x6): + return "8761a"; + case LocalVerUID(RTL_ROM_LMP_8761A, 0xb, 0xa): + return "8761bu"; + case LocalVerUID(RTL_ROM_LMP_8821A, 0xa, 0x6): + return "8821a"; + case LocalVerUID(RTL_ROM_LMP_8821A, 0xc, 0x8): + return "8821c"; + case LocalVerUID(RTL_ROM_LMP_8822B, 0xc, 0xa): + return "8822cu"; + case LocalVerUID(RTL_ROM_LMP_8822B, 0xb, 0x7): + return "8822b"; + case LocalVerUID(RTL_ROM_LMP_8851B, 0xb, 0xc): + return "8851bu"; + case LocalVerUID(RTL_ROM_LMP_8852A, 0xa, 0xb): + return "8852au"; + case LocalVerUID(RTL_ROM_LMP_8852A, 0xb, 0xb): + return "8852bu"; + case LocalVerUID(RTL_ROM_LMP_8852A, 0xc, 0xc): + return "8852cu"; + case LocalVerUID(RTL_ROM_LMP_8852A, 0x87, 0xc): + return "8852btu"; + case LocalVerUID(RTL_ROM_LMP_8922A, 0xa, 0xc): + return "8922au"; + default: + return {}; + } +} + +static std::string GetFirmwareLoadPath() +{ + return File::GetUserPath(D_FIRMWARE_IDX) + "rtl_bt/"; +} + +static void ShowFirmwareReadError(std::string_view fw_path) +{ + PanicAlertFmtT("Bluetooth passthrough failed to read firmware file from:\n" + "{0}\n\n" + "Refer to https://wiki.dolphin-emu.org/index.php?title=Bluetooth_Passthrough " + "for instructions.", + fw_path); +} + +bool InitializeRealtekBluetoothDevice(LibUSBBluetoothAdapter& adapter) +{ + // Read local version. + hci_read_local_ver_rp local_ver{}; + if (!adapter.SendBlockingCommand(Common::AsU8Span(hci_cmd_hdr_t{HCI_CMD_READ_LOCAL_VER, 0}), + Common::AsWritableU8Span(local_ver))) + { + return false; + } + if (local_ver.status != 0x00) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Bad local version status: 0x{:02x}", local_ver.status); + return false; + } + + const auto firmware_name = GetFirmwareName(local_ver); + // If firmware has already been loaded then the subversion changes and we won't find a match. + if (firmware_name.empty()) + { + INFO_LOG_FMT(IOS_WIIMOTE, "Assuming no firmware load is needed"); + return true; + } + + // A name like "8761bu" is turned into "rtl8761bu_fw.bin" + const auto firmware_base_path = GetFirmwareLoadPath() + "rtl" + firmware_name; + + NOTICE_LOG_FMT(IOS_WIIMOTE, "Loading firmware: {}", firmware_name); + + std::string fw_data; + if (const auto fw_path = firmware_base_path + "_fw.bin"; + !File::ReadFileToString(fw_path, fw_data)) + { + ShowFirmwareReadError(fw_path); + return false; + } + + // 8723A is old and the binary is loaded as-is without "parsing". + if (local_ver.lmp_subversion == RTL_ROM_LMP_8723A) + { + // File should *not* have the epatch signature. + if (fw_data.starts_with(RTL_EPATCH_SIGNATURE)) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Unexpected EPATCH signature"); + return false; + } + + return LoadFirmware(adapter, fw_data); + } + + // Read ROM version. + rtl_rom_version_evt read_rom_ver{}; + if (!adapter.SendBlockingCommand(Common::AsU8Span(hci_cmd_hdr_t{0xfc6d, 0}), + Common::AsWritableU8Span(read_rom_ver))) + { + return false; + } + if (read_rom_ver.status != 0x00) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Bad ROM version status: 0x{:02x}", read_rom_ver.status); + return false; + } + + const u8 rom_version = read_rom_ver.version; + INFO_LOG_FMT(IOS_WIIMOTE, "ROM version: 0x{:02x}", rom_version); + + // Apparently only certain devices require the config binary. We load it whenever present. + std::string cfg_data; + File::ReadFileToString(firmware_base_path + "_config.bin", cfg_data); + + const auto firmware = ParseFirmware(fw_data, cfg_data, local_ver, rom_version); + return LoadFirmware(adapter, firmware); +} diff --git a/Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.h b/Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.h new file mode 100644 index 0000000000..c36c03553a --- /dev/null +++ b/Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.h @@ -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&); diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index bc865343e0..97051d034d 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -404,6 +404,7 @@ + @@ -1083,6 +1084,7 @@ +