diff options
Diffstat (limited to 'src/core')
66 files changed, 2133 insertions, 936 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9dbe5bdca..405a2f993 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2,18 +2,8 @@ # SPDX-License-Identifier: GPL-2.0-or-later add_library(core STATIC - announce_multiplayer_session.cpp - announce_multiplayer_session.h arm/arm_interface.h arm/arm_interface.cpp - arm/cpu_interrupt_handler.cpp - arm/cpu_interrupt_handler.h - arm/dynarmic/arm_dynarmic_32.cpp - arm/dynarmic/arm_dynarmic_32.h - arm/dynarmic/arm_dynarmic_64.cpp - arm/dynarmic/arm_dynarmic_64.h - arm/dynarmic/arm_dynarmic_cp15.cpp - arm/dynarmic/arm_dynarmic_cp15.h arm/dynarmic/arm_exclusive_monitor.cpp arm/dynarmic/arm_exclusive_monitor.h arm/exclusive_monitor.cpp @@ -504,9 +494,10 @@ add_library(core STATIC hle/service/jit/jit.h hle/service/lbl/lbl.cpp hle/service/lbl/lbl.h - hle/service/ldn/errors.h + hle/service/ldn/ldn_results.h hle/service/ldn/ldn.cpp hle/service/ldn/ldn.h + hle/service/ldn/ldn_types.h hle/service/ldr/ldr.cpp hle/service/ldr/ldr.h hle/service/lm/lm.cpp @@ -541,14 +532,14 @@ add_library(core STATIC hle/service/npns/npns.cpp hle/service/npns/npns.h hle/service/ns/errors.h + hle/service/ns/iplatform_service_manager.cpp + hle/service/ns/iplatform_service_manager.h hle/service/ns/language.cpp hle/service/ns/language.h hle/service/ns/ns.cpp hle/service/ns/ns.h hle/service/ns/pdm_qry.cpp hle/service/ns/pdm_qry.h - hle/service/ns/pl_u.cpp - hle/service/ns/pl_u.h hle/service/nvdrv/devices/nvdevice.h hle/service/nvdrv/devices/nvdisp_disp0.cpp hle/service/nvdrv/devices/nvdisp_disp0.h @@ -725,6 +716,8 @@ add_library(core STATIC internal_network/network_interface.cpp internal_network/network_interface.h internal_network/sockets.h + internal_network/socket_proxy.cpp + internal_network/socket_proxy.h loader/deconstructed_rom_directory.cpp loader/deconstructed_rom_directory.h loader/kip.cpp @@ -785,7 +778,7 @@ endif() create_target_directory_groups(core) target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) -target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus) +target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus) if (MINGW) target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) endif() diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp deleted file mode 100644 index d73a488cf..000000000 --- a/src/core/announce_multiplayer_session.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <chrono> -#include <future> -#include <vector> -#include "announce_multiplayer_session.h" -#include "common/announce_multiplayer_room.h" -#include "common/assert.h" -#include "common/settings.h" -#include "network/network.h" - -#ifdef ENABLE_WEB_SERVICE -#include "web_service/announce_room_json.h" -#endif - -namespace Core { - -// Time between room is announced to web_service -static constexpr std::chrono::seconds announce_time_interval(15); - -AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& room_network_) - : room_network{room_network_} { -#ifdef ENABLE_WEB_SERVICE - backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(), - Settings::values.yuzu_username.GetValue(), - Settings::values.yuzu_token.GetValue()); -#else - backend = std::make_unique<AnnounceMultiplayerRoom::NullBackend>(); -#endif -} - -WebService::WebResult AnnounceMultiplayerSession::Register() { - std::shared_ptr<Network::Room> room = room_network.GetRoom().lock(); - if (!room) { - return WebService::WebResult{WebService::WebResult::Code::LibError, - "Network is not initialized", ""}; - } - if (room->GetState() != Network::Room::State::Open) { - return WebService::WebResult{WebService::WebResult::Code::LibError, "Room is not open", ""}; - } - UpdateBackendData(room); - WebService::WebResult result = backend->Register(); - if (result.result_code != WebService::WebResult::Code::Success) { - return result; - } - LOG_INFO(WebService, "Room has been registered"); - room->SetVerifyUID(result.returned_data); - registered = true; - return WebService::WebResult{WebService::WebResult::Code::Success, "", ""}; -} - -void AnnounceMultiplayerSession::Start() { - if (announce_multiplayer_thread) { - Stop(); - } - shutdown_event.Reset(); - announce_multiplayer_thread = - std::make_unique<std::thread>(&AnnounceMultiplayerSession::AnnounceMultiplayerLoop, this); -} - -void AnnounceMultiplayerSession::Stop() { - if (announce_multiplayer_thread) { - shutdown_event.Set(); - announce_multiplayer_thread->join(); - announce_multiplayer_thread.reset(); - backend->Delete(); - registered = false; - } -} - -AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback( - std::function<void(const WebService::WebResult&)> function) { - std::lock_guard lock(callback_mutex); - auto handle = std::make_shared<std::function<void(const WebService::WebResult&)>>(function); - error_callbacks.insert(handle); - return handle; -} - -void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) { - std::lock_guard lock(callback_mutex); - error_callbacks.erase(handle); -} - -AnnounceMultiplayerSession::~AnnounceMultiplayerSession() { - Stop(); -} - -void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr<Network::Room> room) { - Network::RoomInformation room_information = room->GetRoomInformation(); - std::vector<AnnounceMultiplayerRoom::Member> memberlist = room->GetRoomMemberList(); - backend->SetRoomInformation(room_information.name, room_information.description, - room_information.port, room_information.member_slots, - Network::network_version, room->HasPassword(), - room_information.preferred_game); - backend->ClearPlayers(); - for (const auto& member : memberlist) { - backend->AddPlayer(member); - } -} - -void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { - // Invokes all current bound error callbacks. - const auto ErrorCallback = [this](WebService::WebResult result) { - std::lock_guard<std::mutex> lock(callback_mutex); - for (auto callback : error_callbacks) { - (*callback)(result); - } - }; - - if (!registered) { - WebService::WebResult result = Register(); - if (result.result_code != WebService::WebResult::Code::Success) { - ErrorCallback(result); - return; - } - } - - auto update_time = std::chrono::steady_clock::now(); - std::future<WebService::WebResult> future; - while (!shutdown_event.WaitUntil(update_time)) { - update_time += announce_time_interval; - std::shared_ptr<Network::Room> room = room_network.GetRoom().lock(); - if (!room) { - break; - } - if (room->GetState() != Network::Room::State::Open) { - break; - } - UpdateBackendData(room); - WebService::WebResult result = backend->Update(); - if (result.result_code != WebService::WebResult::Code::Success) { - ErrorCallback(result); - } - if (result.result_string == "404") { - registered = false; - // Needs to register the room again - WebService::WebResult register_result = Register(); - if (register_result.result_code != WebService::WebResult::Code::Success) { - ErrorCallback(register_result); - } - } - } -} - -AnnounceMultiplayerRoom::RoomList AnnounceMultiplayerSession::GetRoomList() { - return backend->GetRoomList(); -} - -bool AnnounceMultiplayerSession::IsRunning() const { - return announce_multiplayer_thread != nullptr; -} - -void AnnounceMultiplayerSession::UpdateCredentials() { - ASSERT_MSG(!IsRunning(), "Credentials can only be updated when session is not running"); - -#ifdef ENABLE_WEB_SERVICE - backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(), - Settings::values.yuzu_username.GetValue(), - Settings::values.yuzu_token.GetValue()); -#endif -} - -} // namespace Core diff --git a/src/core/announce_multiplayer_session.h b/src/core/announce_multiplayer_session.h deleted file mode 100644 index db790f7d2..000000000 --- a/src/core/announce_multiplayer_session.h +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <atomic> -#include <functional> -#include <memory> -#include <mutex> -#include <set> -#include <thread> -#include "common/announce_multiplayer_room.h" -#include "common/common_types.h" -#include "common/thread.h" - -namespace Network { -class Room; -class RoomNetwork; -} // namespace Network - -namespace Core { - -/** - * Instruments AnnounceMultiplayerRoom::Backend. - * Creates a thread that regularly updates the room information and submits them - * An async get of room information is also possible - */ -class AnnounceMultiplayerSession { -public: - using CallbackHandle = std::shared_ptr<std::function<void(const WebService::WebResult&)>>; - AnnounceMultiplayerSession(Network::RoomNetwork& room_network_); - ~AnnounceMultiplayerSession(); - - /** - * Allows to bind a function that will get called if the announce encounters an error - * @param function The function that gets called - * @return A handle that can be used the unbind the function - */ - CallbackHandle BindErrorCallback(std::function<void(const WebService::WebResult&)> function); - - /** - * Unbind a function from the error callbacks - * @param handle The handle for the function that should get unbind - */ - void UnbindErrorCallback(CallbackHandle handle); - - /** - * Registers a room to web services - * @return The result of the registration attempt. - */ - WebService::WebResult Register(); - - /** - * Starts the announce of a room to web services - */ - void Start(); - - /** - * Stops the announce to web services - */ - void Stop(); - - /** - * Returns a list of all room information the backend got - * @param func A function that gets executed when the async get finished, e.g. a signal - * @return a list of rooms received from the web service - */ - AnnounceMultiplayerRoom::RoomList GetRoomList(); - - /** - * Whether the announce session is still running - */ - bool IsRunning() const; - - /** - * Recreates the backend, updating the credentials. - * This can only be used when the announce session is not running. - */ - void UpdateCredentials(); - -private: - void UpdateBackendData(std::shared_ptr<Network::Room> room); - void AnnounceMultiplayerLoop(); - - Common::Event shutdown_event; - std::mutex callback_mutex; - std::set<CallbackHandle> error_callbacks; - std::unique_ptr<std::thread> announce_multiplayer_thread; - - /// Backend interface that logs fields - std::unique_ptr<AnnounceMultiplayerRoom::Backend> backend; - - std::atomic_bool registered = false; ///< Whether the room has been registered - - Network::RoomNetwork& room_network; -}; - -} // namespace Core diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 73f259525..7d62d030e 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -27,7 +27,6 @@ namespace Core { class System; class CPUInterruptHandler; -using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>; using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>; /// Generic ARMv8 CPU interface @@ -36,10 +35,8 @@ public: YUZU_NON_COPYABLE(ARM_Interface); YUZU_NON_MOVEABLE(ARM_Interface); - explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_, - bool uses_wall_clock_) - : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{ - uses_wall_clock_} {} + explicit ARM_Interface(System& system_, bool uses_wall_clock_) + : system{system_}, uses_wall_clock{uses_wall_clock_} {} virtual ~ARM_Interface() = default; struct ThreadContext32 { @@ -181,6 +178,9 @@ public: /// Signal an interrupt and ask the core to halt as soon as possible. virtual void SignalInterrupt() = 0; + /// Clear a previous interrupt. + virtual void ClearInterrupt() = 0; + struct BacktraceEntry { std::string module; u64 address; @@ -208,7 +208,6 @@ public: protected: /// System context that this ARM interface is running under. System& system; - CPUInterrupts& interrupt_handlers; const WatchpointArray* watchpoints; bool uses_wall_clock; diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp deleted file mode 100644 index 77b6194d7..000000000 --- a/src/core/arm/cpu_interrupt_handler.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/thread.h" -#include "core/arm/cpu_interrupt_handler.h" - -namespace Core { - -CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {} - -CPUInterruptHandler::~CPUInterruptHandler() = default; - -void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) { - if (is_interrupted_) { - interrupt_event->Set(); - } - is_interrupted = is_interrupted_; -} - -void CPUInterruptHandler::AwaitInterrupt() { - interrupt_event->Wait(); -} - -} // namespace Core diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h deleted file mode 100644 index 286e12e53..000000000 --- a/src/core/arm/cpu_interrupt_handler.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <atomic> -#include <memory> - -namespace Common { -class Event; -} - -namespace Core { - -class CPUInterruptHandler { -public: - CPUInterruptHandler(); - ~CPUInterruptHandler(); - - CPUInterruptHandler(const CPUInterruptHandler&) = delete; - CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete; - - CPUInterruptHandler(CPUInterruptHandler&&) = delete; - CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete; - - bool IsInterrupted() const { - return is_interrupted; - } - - void SetInterrupt(bool is_interrupted); - - void AwaitInterrupt(); - -private: - std::unique_ptr<Common::Event> interrupt_event; - std::atomic_bool is_interrupted{false}; -}; - -} // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index b8d2ce224..d1e70f19d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -11,7 +11,6 @@ #include "common/logging/log.h" #include "common/page_table.h" #include "common/settings.h" -#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" #include "core/arm/dynarmic/arm_dynarmic_cp15.h" #include "core/arm/dynarmic/arm_exclusive_monitor.h" @@ -125,7 +124,9 @@ public: } void AddTicks(u64 ticks) override { - ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); + if (parent.uses_wall_clock) { + return; + } // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a // rough approximation of the amount of executed ticks in the system, it may be thrown off @@ -142,7 +143,12 @@ public: } u64 GetTicksRemaining() override { - ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); + if (parent.uses_wall_clock) { + if (!IsInterrupted()) { + return minimum_run_cycles; + } + return 0U; + } return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); } @@ -168,11 +174,15 @@ public: parent.jit.load()->HaltExecution(hr); } + bool IsInterrupted() { + return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted(); + } + ARM_Dynarmic_32& parent; Core::Memory::Memory& memory; std::size_t num_interpreted_instructions{}; bool debugger_enabled{}; - static constexpr u64 minimum_run_cycles = 1000U; + static constexpr u64 minimum_run_cycles = 10000U; }; std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const { @@ -180,19 +190,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* config.callbacks = cb.get(); config.coprocessors[15] = cp15; config.define_unpredictable_behaviour = true; - static constexpr std::size_t PAGE_BITS = 12; - static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS); + static constexpr std::size_t YUZU_PAGEBITS = 12; + static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - YUZU_PAGEBITS); if (page_table) { config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( page_table->pointers.data()); + config.absolute_offset_page_table = true; + config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; + config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; + config.only_detect_misalignment_via_page_table_on_page_boundary = true; + config.fastmem_pointer = page_table->fastmem_arena; + + config.fastmem_exclusive_access = config.fastmem_pointer != nullptr; + config.recompile_on_exclusive_fastmem_failure = true; } - config.absolute_offset_page_table = true; - config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; - config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; - config.only_detect_misalignment_via_page_table_on_page_boundary = true; - config.fastmem_exclusive_access = true; - config.recompile_on_exclusive_fastmem_failure = true; // Multi-process state config.processor_id = core_index; @@ -200,7 +212,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* // Timing config.wall_clock_cntpct = uses_wall_clock; - config.enable_cycle_counting = !uses_wall_clock; + config.enable_cycle_counting = true; // Code cache size config.code_cache_size = 512_MiB; @@ -244,6 +256,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* } if (!Settings::values.cpuopt_fastmem) { config.fastmem_pointer = nullptr; + config.fastmem_exclusive_access = false; } if (!Settings::values.cpuopt_fastmem_exclusives) { config.fastmem_exclusive_access = false; @@ -311,11 +324,9 @@ void ARM_Dynarmic_32::RewindBreakpointInstruction() { LoadContext(breakpoint_context); } -ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, - bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, - std::size_t core_index_) - : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_}, - cb(std::make_unique<DynarmicCallbacks32>(*this)), +ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, + ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_) + : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)), cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {} @@ -394,6 +405,10 @@ void ARM_Dynarmic_32::SignalInterrupt() { jit.load()->HaltExecution(break_loop); } +void ARM_Dynarmic_32::ClearInterrupt() { + jit.load()->ClearHalt(break_loop); +} + void ARM_Dynarmic_32::ClearInstructionCache() { jit.load()->ClearCache(); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 346e9abf8..d24ba2289 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -28,8 +28,8 @@ class System; class ARM_Dynarmic_32 final : public ARM_Interface { public: - ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_, - ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); + ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, + std::size_t core_index_); ~ARM_Dynarmic_32() override; void SetPC(u64 pc) override; @@ -56,6 +56,7 @@ public: void LoadContext(const ThreadContext64& ctx) override {} void SignalInterrupt() override; + void ClearInterrupt() override; void ClearExclusiveState() override; void ClearInstructionCache() override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 1a4d37cbc..1d46f6d40 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -10,7 +10,6 @@ #include "common/logging/log.h" #include "common/page_table.h" #include "common/settings.h" -#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/arm/dynarmic/arm_exclusive_monitor.h" #include "core/core.h" @@ -166,7 +165,9 @@ public: } void AddTicks(u64 ticks) override { - ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); + if (parent.uses_wall_clock) { + return; + } // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a // rough approximation of the amount of executed ticks in the system, it may be thrown off @@ -181,7 +182,12 @@ public: } u64 GetTicksRemaining() override { - ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); + if (parent.uses_wall_clock) { + if (!IsInterrupted()) { + return minimum_run_cycles; + } + return 0U; + } return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); } @@ -211,12 +217,16 @@ public: parent.jit.load()->HaltExecution(hr); } + bool IsInterrupted() { + return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted(); + } + ARM_Dynarmic_64& parent; Core::Memory::Memory& memory; u64 tpidrro_el0 = 0; u64 tpidr_el0 = 0; bool debugger_enabled{}; - static constexpr u64 minimum_run_cycles = 1000U; + static constexpr u64 minimum_run_cycles = 10000U; }; std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table, @@ -240,7 +250,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* config.fastmem_address_space_bits = address_space_bits; config.silently_mirror_fastmem = false; - config.fastmem_exclusive_access = true; + config.fastmem_exclusive_access = config.fastmem_pointer != nullptr; config.recompile_on_exclusive_fastmem_failure = true; } @@ -260,7 +270,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* // Timing config.wall_clock_cntpct = uses_wall_clock; - config.enable_cycle_counting = !uses_wall_clock; + config.enable_cycle_counting = true; // Code cache size config.code_cache_size = 512_MiB; @@ -304,6 +314,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* } if (!Settings::values.cpuopt_fastmem) { config.fastmem_pointer = nullptr; + config.fastmem_exclusive_access = false; } if (!Settings::values.cpuopt_fastmem_exclusives) { config.fastmem_exclusive_access = false; @@ -371,10 +382,9 @@ void ARM_Dynarmic_64::RewindBreakpointInstruction() { LoadContext(breakpoint_context); } -ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, - bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, - std::size_t core_index_) - : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_}, +ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, + ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_) + : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {} @@ -461,6 +471,10 @@ void ARM_Dynarmic_64::SignalInterrupt() { jit.load()->HaltExecution(break_loop); } +void ARM_Dynarmic_64::ClearInterrupt() { + jit.load()->ClearHalt(break_loop); +} + void ARM_Dynarmic_64::ClearInstructionCache() { jit.load()->ClearCache(); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index c77a83ad7..ed1a5eb96 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -20,14 +20,13 @@ class Memory; namespace Core { class DynarmicCallbacks64; -class CPUInterruptHandler; class DynarmicExclusiveMonitor; class System; class ARM_Dynarmic_64 final : public ARM_Interface { public: - ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_, - ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); + ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, + std::size_t core_index_); ~ARM_Dynarmic_64() override; void SetPC(u64 pc) override; @@ -50,6 +49,7 @@ public: void LoadContext(const ThreadContext64& ctx) override; void SignalInterrupt() override; + void ClearInterrupt() override; void ClearExclusiveState() override; void ClearInstructionCache() override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index e9123c13d..200efe4db 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp @@ -8,6 +8,10 @@ #include "core/core.h" #include "core/core_timing.h" +#ifdef _MSC_VER +#include <intrin.h> +#endif + using Callback = Dynarmic::A32::Coprocessor::Callback; using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord; using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords; @@ -47,12 +51,31 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1 switch (opc2) { case 4: // CP15_DATA_SYNC_BARRIER - // This is a dummy write, we ignore the value written here. - return &dummy_value; + return Callback{ + [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { +#ifdef _MSC_VER + _mm_mfence(); + _mm_lfence(); +#else + asm volatile("mfence\n\tlfence\n\t" : : : "memory"); +#endif + return 0; + }, + std::nullopt, + }; case 5: // CP15_DATA_MEMORY_BARRIER - // This is a dummy write, we ignore the value written here. - return &dummy_value; + return Callback{ + [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { +#ifdef _MSC_VER + _mm_mfence(); +#else + asm volatile("mfence\n\t" : : : "memory"); +#endif + return 0; + }, + std::nullopt, + }; } } diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h index 5b2a51636..d90b3e568 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h @@ -35,6 +35,8 @@ public: ARM_Dynarmic_32& parent; u32 uprw = 0; u32 uro = 0; + + friend class ARM_Dynarmic_32; }; } // namespace Core diff --git a/src/core/core.cpp b/src/core/core.cpp index ea32a4a8d..121092868 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -141,8 +141,6 @@ struct System::Impl { core_timing.SyncPause(false); is_paused = false; - audio_core->PauseSinks(false); - return status; } @@ -150,8 +148,6 @@ struct System::Impl { std::unique_lock<std::mutex> lk(suspend_guard); status = SystemResultStatus::Success; - audio_core->PauseSinks(true); - core_timing.SyncPause(true); kernel.Suspend(true); is_paused = true; @@ -319,10 +315,19 @@ struct System::Impl { if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result); } + + std::string title_version; + const FileSys::PatchManager pm(program_id, system.GetFileSystemController(), + system.GetContentProvider()); + const auto metadata = pm.GetControlMetadata(); + if (metadata.first != nullptr) { + title_version = metadata.first->GetVersionString(); + } if (auto room_member = room_network.GetRoomMember().lock()) { Network::GameInfo game_info; game_info.name = name; game_info.id = program_id; + game_info.version = title_version; room_member->SendGameInfo(game_info); } diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 8d1ee3b51..f6c4567ba 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -73,7 +73,6 @@ void CoreTiming::Shutdown() { if (timer_thread) { timer_thread->join(); } - pause_callbacks.clear(); ClearPendingEvents(); timer_thread.reset(); has_started = false; @@ -86,10 +85,6 @@ void CoreTiming::Pause(bool is_paused) { if (!is_paused) { pause_end_time = GetGlobalTimeNs().count(); } - - for (auto& cb : pause_callbacks) { - cb(is_paused); - } } void CoreTiming::SyncPause(bool is_paused) { @@ -110,10 +105,6 @@ void CoreTiming::SyncPause(bool is_paused) { if (!is_paused) { pause_end_time = GetGlobalTimeNs().count(); } - - for (auto& cb : pause_callbacks) { - cb(is_paused); - } } bool CoreTiming::IsRunning() const { @@ -223,11 +214,6 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { } } -void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) { - std::scoped_lock lock{basic_lock}; - pause_callbacks.emplace_back(std::move(callback)); -} - std::optional<s64> CoreTiming::Advance() { std::scoped_lock lock{advance_lock, basic_lock}; global_timer = GetGlobalTimeNs().count(); diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 6aa3ae923..3259397b2 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -22,7 +22,6 @@ namespace Core::Timing { /// A callback that may be scheduled for a particular core timing event. using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; -using PauseCallback = std::function<void(bool paused)>; /// Contains the characteristics of a particular event. struct EventType { @@ -134,9 +133,6 @@ public: /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. std::optional<s64> Advance(); - /// Register a callback function to be called when coretiming pauses. - void RegisterPauseCallback(PauseCallback&& callback); - private: struct Event; @@ -176,8 +172,6 @@ private: /// Cycle timing u64 ticks{}; s64 downcount{}; - - std::vector<PauseCallback> pause_callbacks{}; }; /// Creates a core timing event with the given name and callback. diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index ac64d2f9d..e42bdd17d 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -15,6 +15,7 @@ #include "core/debugger/debugger_interface.h" #include "core/debugger/gdbstub.h" #include "core/hle/kernel/global_scheduler_context.h" +#include "core/hle/kernel/k_scheduler.h" template <typename Readable, typename Buffer, typename Callback> static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { @@ -230,13 +231,12 @@ private: } void PauseEmulation() { + Kernel::KScopedSchedulerLock sl{system.Kernel()}; + // Put all threads to sleep on next scheduler round. for (auto* thread : ThreadList()) { thread->RequestSuspend(Kernel::SuspendType::Debug); } - - // Signal an interrupt so that scheduler will fire. - system.Kernel().InterruptAllPhysicalCores(); } void ResumeEmulation(Kernel::KThread* except = nullptr) { @@ -253,7 +253,8 @@ private: template <typename Callback> void MarkResumed(Callback&& cb) { - std::scoped_lock lk{connection_lock}; + Kernel::KScopedSchedulerLock sl{system.Kernel()}; + std::scoped_lock cl{connection_lock}; stopped = false; cb(); } diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 4b35ca82f..5aab428bb 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp @@ -217,9 +217,7 @@ void IPSwitchCompiler::Parse() { break; } else if (StartsWith(line, "@nsobid-")) { // NSO Build ID Specifier - auto raw_build_id = line.substr(8); - if (raw_build_id.size() != 0x40) - raw_build_id.resize(0x40, '0'); + const auto raw_build_id = fmt::format("{:0<64}", line.substr(8)); nso_build_id = Common::HexStringToArray<0x20>(raw_build_id); } else if (StartsWith(line, "#")) { // Mandatory Comment @@ -287,7 +285,8 @@ void IPSwitchCompiler::Parse() { std::copy(value.begin(), value.end(), std::back_inserter(replace)); } else { // hex replacement - const auto value = patch_line.substr(9); + const auto value = + patch_line.substr(9, patch_line.find_first_of(" /\r\n", 9) - 9); replace = Common::HexStringToVector(value, is_little_endian); } diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index bd525b26c..4c80e13a9 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -191,6 +191,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const { const auto& disabled = Settings::values.disabled_addons[title_id]; + const auto nso_build_id = fmt::format("{:0<64}", build_id); std::vector<VirtualFile> out; out.reserve(patch_dirs.size()); @@ -203,21 +204,18 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD for (const auto& file : exefs_dir->GetFiles()) { if (file->GetExtension() == "ips") { auto name = file->GetName(); - const auto p1 = name.substr(0, name.find('.')); - const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); - if (build_id == this_build_id) + const auto this_build_id = + fmt::format("{:0<64}", name.substr(0, name.find('.'))); + if (nso_build_id == this_build_id) out.push_back(file); } else if (file->GetExtension() == "pchtxt") { IPSwitchCompiler compiler{file}; if (!compiler.IsValid()) continue; - auto this_build_id = Common::HexToString(compiler.GetBuildID()); - this_build_id = - this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1); - - if (build_id == this_build_id) + const auto this_build_id = Common::HexToString(compiler.GetBuildID()); + if (nso_build_id == this_build_id) out.push_back(file); } } diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp index f841988ff..3210583f0 100644 --- a/src/core/file_sys/system_archive/shared_font.cpp +++ b/src/core/file_sys/system_archive/shared_font.cpp @@ -9,7 +9,7 @@ #include "core/file_sys/system_archive/data/font_standard.h" #include "core/file_sys/system_archive/shared_font.h" #include "core/file_sys/vfs_vector.h" -#include "core/hle/service/ns/pl_u.h" +#include "core/hle/service/ns/iplatform_service_manager.h" namespace FileSys::SystemArchive { diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 8c3895937..01c43be93 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/thread.h" #include "core/hid/emulated_controller.h" #include "core/hid/input_converter.h" @@ -84,23 +85,26 @@ void EmulatedController::ReloadFromSettings() { motion_params[index] = Common::ParamPackage(player.motions[index]); } + controller.colors_state.fullkey = { + .body = GetNpadColor(player.body_color_left), + .button = GetNpadColor(player.button_color_left), + }; controller.colors_state.left = { - .body = player.body_color_left, - .button = player.button_color_left, + .body = GetNpadColor(player.body_color_left), + .button = GetNpadColor(player.button_color_left), }; - - controller.colors_state.right = { - .body = player.body_color_right, - .button = player.button_color_right, + controller.colors_state.left = { + .body = GetNpadColor(player.body_color_right), + .button = GetNpadColor(player.button_color_right), }; - controller.colors_state.fullkey = controller.colors_state.left; - // Other or debug controller should always be a pro controller if (npad_id_type != NpadIdType::Other) { SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); + original_npad_type = npad_type; } else { SetNpadStyleIndex(NpadStyleIndex::ProController); + original_npad_type = npad_type; } if (player.connected) { @@ -352,6 +356,7 @@ void EmulatedController::DisableConfiguration() { Disconnect(); } SetNpadStyleIndex(tmp_npad_type); + original_npad_type = tmp_npad_type; } // Apply temporary connected status to the real controller @@ -557,6 +562,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback return; } + // GC controllers have triggers not buttons + if (npad_type == NpadStyleIndex::GameCube) { + if (index == Settings::NativeButton::ZR) { + return; + } + if (index == Settings::NativeButton::ZL) { + return; + } + } + switch (index) { case Settings::NativeButton::A: controller.npad_button_state.a.Assign(current_status.value); @@ -733,6 +748,11 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac return; } + // Only GC controllers have analog triggers + if (npad_type != NpadStyleIndex::GameCube) { + return; + } + const auto& trigger = controller.trigger_values[index]; switch (index) { @@ -949,6 +969,9 @@ bool EmulatedController::TestVibration(std::size_t device_index) { // Send a slight vibration to test for rumble support output_devices[device_index]->SetVibration(test_vibration); + // Wait for about 15ms to ensure the controller is ready for the stop command + std::this_thread::sleep_for(std::chrono::milliseconds(15)); + // Stop any vibration and return the result return output_devices[device_index]->SetVibration(zero_vibration) == Common::Input::VibrationError::None; @@ -999,13 +1022,27 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) if (!is_connected) { return; } + + // Attempt to reconnect with the original type + if (npad_type != original_npad_type) { + Disconnect(); + const auto current_npad_type = npad_type; + SetNpadStyleIndex(original_npad_type); + if (IsControllerSupported()) { + Connect(); + return; + } + SetNpadStyleIndex(current_npad_type); + Connect(); + } + if (IsControllerSupported()) { return; } Disconnect(); - // Fallback fullkey controllers to Pro controllers + // Fallback Fullkey controllers to Pro controllers if (IsControllerFullkey() && supported_style_tag.fullkey) { LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); SetNpadStyleIndex(NpadStyleIndex::ProController); @@ -1013,6 +1050,22 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) return; } + // Fallback Dual joycon controllers to Pro controllers + if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) { + LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); + SetNpadStyleIndex(NpadStyleIndex::ProController); + Connect(); + return; + } + + // Fallback Pro controllers to Dual joycon + if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) { + LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type); + SetNpadStyleIndex(NpadStyleIndex::JoyconDual); + Connect(); + return; + } + LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", npad_type); } @@ -1310,6 +1363,15 @@ const CameraState& EmulatedController::GetCamera() const { return controller.camera_state; } +NpadColor EmulatedController::GetNpadColor(u32 color) { + return { + .r = static_cast<u8>((color >> 16) & 0xFF), + .g = static_cast<u8>((color >> 8) & 0xFF), + .b = static_cast<u8>(color & 0xFF), + .a = 0xff, + }; +} + void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { std::scoped_lock lock{callback_mutex}; for (const auto& poller_pair : callback_list) { diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index 823c1700c..c3aa8f9d3 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -425,6 +425,13 @@ private: void SetCamera(const Common::Input::CallbackStatus& callback); /** + * Converts a color format from bgra to rgba + * @param color in bgra format + * @return NpadColor in rgba format + */ + NpadColor GetNpadColor(u32 color); + + /** * Triggers a callback that something has changed on the controller status * @param type Input type of the event to trigger * @param is_service_update indicates if this event should only be sent to HID services @@ -433,6 +440,7 @@ private: const NpadIdType npad_id_type; NpadStyleIndex npad_type{NpadStyleIndex::None}; + NpadStyleIndex original_npad_type{NpadStyleIndex::None}; NpadStyleTag supported_style_tag{NpadStyleSet::All}; bool is_connected{false}; bool is_configuring{false}; diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index e49223016..e3b1cfbc6 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h @@ -327,10 +327,18 @@ struct TouchState { }; static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); +struct NpadColor { + u8 r{}; + u8 g{}; + u8 b{}; + u8 a{}; +}; +static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size"); + // This is nn::hid::NpadControllerColor struct NpadControllerColor { - u32 body{}; - u32 button{}; + NpadColor body{}; + NpadColor button{}; }; static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size"); diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index 68d143a01..52fb69e9c 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp @@ -52,6 +52,9 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu Common::Input::ButtonStatus status{}; switch (callback.type) { case Common::Input::InputType::Analog: + status.value = TransformToTrigger(callback).pressed.value; + status.toggle = callback.analog_status.properties.toggle; + break; case Common::Input::InputType::Trigger: status.value = TransformToTrigger(callback).pressed.value; break; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index f4072e1c3..ce7fa8275 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -17,7 +17,6 @@ #include "common/thread.h" #include "common/thread_worker.h" #include "core/arm/arm_interface.h" -#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" @@ -82,7 +81,7 @@ struct KernelCore::Impl { void InitializeCores() { for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - cores[core_id].Initialize((*current_process).Is64BitProcess()); + cores[core_id]->Initialize((*current_process).Is64BitProcess()); system.Memory().SetCurrentPageTable(*current_process, core_id); } } @@ -100,7 +99,9 @@ struct KernelCore::Impl { next_user_process_id = KProcess::ProcessIDMin; next_thread_id = 1; - cores.clear(); + for (auto& core : cores) { + core = nullptr; + } global_handle_table->Finalize(); global_handle_table.reset(); @@ -199,7 +200,7 @@ struct KernelCore::Impl { const s32 core{static_cast<s32>(i)}; schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel()); - cores.emplace_back(i, system, *schedulers[i], interrupts); + cores[i] = std::make_unique<Kernel::PhysicalCore>(i, system, *schedulers[i]); auto* main_thread{Kernel::KThread::Create(system.Kernel())}; main_thread->SetName(fmt::format("MainThread:{}", core)); @@ -761,7 +762,7 @@ struct KernelCore::Impl { std::unordered_set<KAutoObject*> registered_in_use_objects; std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; - std::vector<Kernel::PhysicalCore> cores; + std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores; // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES}; @@ -785,7 +786,6 @@ struct KernelCore::Impl { Common::ThreadWorker service_threads_manager; std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; - std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; bool is_multicore{}; @@ -874,11 +874,11 @@ const Kernel::KScheduler& KernelCore::Scheduler(std::size_t id) const { } Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { - return impl->cores[id]; + return *impl->cores[id]; } const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { - return impl->cores[id]; + return *impl->cores[id]; } size_t KernelCore::CurrentPhysicalCoreIndex() const { @@ -890,11 +890,11 @@ size_t KernelCore::CurrentPhysicalCoreIndex() const { } Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { - return impl->cores[CurrentPhysicalCoreIndex()]; + return *impl->cores[CurrentPhysicalCoreIndex()]; } const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { - return impl->cores[CurrentPhysicalCoreIndex()]; + return *impl->cores[CurrentPhysicalCoreIndex()]; } Kernel::KScheduler* KernelCore::CurrentScheduler() { @@ -906,15 +906,6 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() { return impl->schedulers[core_id].get(); } -std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() { - return impl->interrupts; -} - -const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() - const { - return impl->interrupts; -} - Kernel::TimeManager& KernelCore::TimeManager() { return impl->time_manager; } @@ -939,24 +930,18 @@ const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { return *impl->global_object_list_container; } -void KernelCore::InterruptAllPhysicalCores() { - for (auto& physical_core : impl->cores) { - physical_core.Interrupt(); - } -} - void KernelCore::InvalidateAllInstructionCaches() { for (auto& physical_core : impl->cores) { - physical_core.ArmInterface().ClearInstructionCache(); + physical_core->ArmInterface().ClearInstructionCache(); } } void KernelCore::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) { for (auto& physical_core : impl->cores) { - if (!physical_core.IsInitialized()) { + if (!physical_core->IsInitialized()) { continue; } - physical_core.ArmInterface().InvalidateCacheRange(addr, size); + physical_core->ArmInterface().InvalidateCacheRange(addr, size); } } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 6c7cf6af2..bcf016a97 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -9,14 +9,12 @@ #include <string> #include <unordered_map> #include <vector> -#include "core/arm/cpu_interrupt_handler.h" #include "core/hardware_properties.h" #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_slab_heap.h" #include "core/hle/kernel/svc_common.h" namespace Core { -class CPUInterruptHandler; class ExclusiveMonitor; class System; } // namespace Core @@ -183,12 +181,6 @@ public: const KAutoObjectWithListContainer& ObjectListContainer() const; - std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts(); - - const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const; - - void InterruptAllPhysicalCores(); - void InvalidateAllInstructionCaches(); void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size); diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index 6e7dacf97..d4375962f 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/arm/cpu_interrupt_handler.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" #include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/core.h" @@ -11,16 +10,14 @@ namespace Kernel { -PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, - Core::CPUInterrupts& interrupts_) - : core_index{core_index_}, system{system_}, scheduler{scheduler_}, - interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} { +PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_) + : core_index{core_index_}, system{system_}, scheduler{scheduler_} { #ifdef ARCHITECTURE_x86_64 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. auto& kernel = system.Kernel(); arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( - system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); + system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); #else #error Platform not supported yet. #endif @@ -34,7 +31,7 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { if (!is_64_bit) { // We already initialized a 64-bit core, replace with a 32-bit one. arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( - system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); + system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index); } #else #error Platform not supported yet. @@ -47,24 +44,26 @@ void PhysicalCore::Run() { } void PhysicalCore::Idle() { - interrupts[core_index].AwaitInterrupt(); + std::unique_lock lk{guard}; + on_interrupt.wait(lk, [this] { return is_interrupted; }); } bool PhysicalCore::IsInterrupted() const { - return interrupts[core_index].IsInterrupted(); + return is_interrupted; } void PhysicalCore::Interrupt() { - guard->lock(); - interrupts[core_index].SetInterrupt(true); + std::unique_lock lk{guard}; + is_interrupted = true; arm_interface->SignalInterrupt(); - guard->unlock(); + on_interrupt.notify_all(); } void PhysicalCore::ClearInterrupt() { - guard->lock(); - interrupts[core_index].SetInterrupt(false); - guard->unlock(); + std::unique_lock lk{guard}; + is_interrupted = false; + arm_interface->ClearInterrupt(); + on_interrupt.notify_all(); } } // namespace Kernel diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 898d1e5db..2fc8d4be2 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -14,7 +14,6 @@ class KScheduler; } // namespace Kernel namespace Core { -class CPUInterruptHandler; class ExclusiveMonitor; class System; } // namespace Core @@ -23,15 +22,11 @@ namespace Kernel { class PhysicalCore { public: - PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, - Core::CPUInterrupts& interrupts_); + PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_); ~PhysicalCore(); - PhysicalCore(const PhysicalCore&) = delete; - PhysicalCore& operator=(const PhysicalCore&) = delete; - - PhysicalCore(PhysicalCore&&) = default; - PhysicalCore& operator=(PhysicalCore&&) = delete; + YUZU_NON_COPYABLE(PhysicalCore); + YUZU_NON_MOVEABLE(PhysicalCore); /// Initialize the core for the specified parameters. void Initialize(bool is_64_bit); @@ -86,9 +81,11 @@ private: const std::size_t core_index; Core::System& system; Kernel::KScheduler& scheduler; - Core::CPUInterrupts& interrupts; - std::unique_ptr<std::mutex> guard; + + std::mutex guard; + std::condition_variable on_interrupt; std::unique_ptr<Core::ARM_Interface> arm_interface; + bool is_interrupted; }; } // namespace Kernel diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 4de44cd06..47a1b829b 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -117,6 +117,7 @@ union Result { BitField<0, 9, ErrorModule> module; BitField<9, 13, u32> description; + Result() = default; constexpr explicit Result(u32 raw_) : raw(raw_) {} constexpr Result(ErrorModule module_, u32 description_) @@ -130,6 +131,7 @@ union Result { return !IsSuccess(); } }; +static_assert(std::is_trivial_v<Result>); [[nodiscard]] constexpr bool operator==(const Result& a, const Result& b) { return a.raw == b.raw; diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index def105832..bb838e285 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -534,7 +534,7 @@ public: private: void CheckAvailability(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_DEBUG(Service_ACC, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(false); // TODO: Check when this is supposed to return true and when not diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 118f226e4..6fb7e198e 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -754,7 +754,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { } void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index 4b804b78c..14aa6f69e 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp @@ -21,7 +21,7 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/ns/pl_u.h" +#include "core/hle/service/ns/iplatform_service_manager.h" #include "core/loader/loader.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp index 4e710491b..d6de84066 100644 --- a/src/core/hle/service/apm/apm_controller.cpp +++ b/src/core/hle/service/apm/apm_controller.cpp @@ -80,7 +80,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa } void Controller::SetClockSpeed(u32 mhz) { - LOG_INFO(Service_APM, "called, mhz={:08X}", mhz); + LOG_DEBUG(Service_APM, "called, mhz={:08X}", mhz); // TODO(DarkLordZach): Actually signal core_timing to change clock speed. // TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used. } diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index a44dd842a..49c092301 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -246,9 +246,8 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { const auto write_count = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); std::vector<AudioDevice::AudioDeviceName> device_names{}; - std::string print_names{}; if (write_count > 0) { - device_names.push_back(AudioDevice::AudioDeviceName("DeviceOut")); + device_names.emplace_back("DeviceOut"); LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut"); } else { LOG_DEBUG(Service_Audio, "called. Empty buffer passed in."); diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 381a66ba5..6fb07c37d 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -50,7 +50,7 @@ public: {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, - {10, nullptr, "RequestUpdateAuto"}, + {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"}, {11, nullptr, "ExecuteAudioRendererRendering"}, }; // clang-format on @@ -113,15 +113,30 @@ private: // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for // checking size 0. Performance size is 0 for most games. - const auto buffers{ctx.BufferDescriptorB()}; - std::vector<u8> output(buffers[0].Size(), 0); - std::vector<u8> performance(buffers[1].Size(), 0); + + std::vector<u8> output{}; + std::vector<u8> performance{}; + auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0}; + if (is_buffer_b) { + const auto buffersB{ctx.BufferDescriptorB()}; + output.resize(buffersB[0].Size(), 0); + performance.resize(buffersB[1].Size(), 0); + } else { + const auto buffersC{ctx.BufferDescriptorC()}; + output.resize(buffersC[0].Size(), 0); + performance.resize(buffersC[1].Size(), 0); + } auto result = impl->RequestUpdate(input, performance, output); if (result.IsSuccess()) { - ctx.WriteBufferB(output.data(), output.size(), 0); - ctx.WriteBufferB(performance.data(), performance.size(), 1); + if (is_buffer_b) { + ctx.WriteBufferB(output.data(), output.size(), 0); + ctx.WriteBufferB(performance.data(), performance.size(), 1); + } else { + ctx.WriteBufferC(output.data(), output.size(), 0); + ctx.WriteBufferC(performance.data(), performance.size(), 1); + } } else { LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); } @@ -237,7 +252,7 @@ private: std::vector<AudioDevice::AudioDeviceName> out_names{}; - u32 out_count = impl->ListAudioDeviceName(out_names, in_count); + const u32 out_count = impl->ListAudioDeviceName(out_names, in_count); std::string out{}; for (u32 i = 0; i < out_count; i++) { @@ -350,7 +365,7 @@ private: std::vector<AudioDevice::AudioDeviceName> out_names{}; - u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count); + const u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count); std::string out{}; for (u32 i = 0; i < out_count; i++) { diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index fae6e5aff..e23eae36a 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -246,7 +246,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec entries.reserve(entries.size() + new_data.size()); for (const auto& new_entry : new_data) { - entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize()); + entries.emplace_back(new_entry->GetName(), type, + type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize()); } } diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 3c28dee76..cb29004e8 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -163,28 +163,51 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { } LOG_DEBUG(Service_HID, "Npad connected {}", npad_id); const auto controller_type = controller.device->GetNpadStyleIndex(); + const auto& body_colors = controller.device->GetColors(); + const auto& battery_level = controller.device->GetBattery(); auto* shared_memory = controller.shared_memory; if (controller_type == Core::HID::NpadStyleIndex::None) { controller.styleset_changed_event->GetWritableEvent().Signal(); return; } + + // Reset memory values shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; shared_memory->device_type.raw = 0; shared_memory->system_properties.raw = 0; + shared_memory->joycon_color.attribute = ColorAttribute::NoController; + shared_memory->joycon_color.attribute = ColorAttribute::NoController; + shared_memory->fullkey_color = {}; + shared_memory->joycon_color.left = {}; + shared_memory->joycon_color.right = {}; + shared_memory->battery_level_dual = {}; + shared_memory->battery_level_left = {}; + shared_memory->battery_level_right = {}; + switch (controller_type) { case Core::HID::NpadStyleIndex::None: ASSERT(false); break; case Core::HID::NpadStyleIndex::ProController: + shared_memory->fullkey_color.attribute = ColorAttribute::Ok; + shared_memory->fullkey_color.fullkey = body_colors.fullkey; + shared_memory->battery_level_dual = battery_level.dual.battery_level; shared_memory->style_tag.fullkey.Assign(1); shared_memory->device_type.fullkey.Assign(1); shared_memory->system_properties.is_vertical.Assign(1); shared_memory->system_properties.use_plus.Assign(1); shared_memory->system_properties.use_minus.Assign(1); + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.dual.is_charging); shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController; shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::Handheld: + shared_memory->fullkey_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.attribute = ColorAttribute::Ok; + shared_memory->fullkey_color.fullkey = body_colors.fullkey; + shared_memory->joycon_color.left = body_colors.left; + shared_memory->joycon_color.right = body_colors.right; shared_memory->style_tag.handheld.Assign(1); shared_memory->device_type.handheld_left.Assign(1); shared_memory->device_type.handheld_right.Assign(1); @@ -192,47 +215,86 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { shared_memory->system_properties.use_plus.Assign(1); shared_memory->system_properties.use_minus.Assign(1); shared_memory->system_properties.use_directional_buttons.Assign(1); + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.left.is_charging); + shared_memory->system_properties.is_charging_joy_left.Assign( + battery_level.left.is_charging); + shared_memory->system_properties.is_charging_joy_right.Assign( + battery_level.right.is_charging); shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::HandheldJoyConLeftJoyConRight; shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::JoyconDual: + shared_memory->fullkey_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.attribute = ColorAttribute::Ok; shared_memory->style_tag.joycon_dual.Assign(1); if (controller.is_dual_left_connected) { + shared_memory->joycon_color.left = body_colors.left; + shared_memory->battery_level_left = battery_level.left.battery_level; shared_memory->device_type.joycon_left.Assign(1); shared_memory->system_properties.use_minus.Assign(1); + shared_memory->system_properties.is_charging_joy_left.Assign( + battery_level.left.is_charging); shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1); } if (controller.is_dual_right_connected) { + shared_memory->joycon_color.right = body_colors.right; + shared_memory->battery_level_right = battery_level.right.battery_level; shared_memory->device_type.joycon_right.Assign(1); shared_memory->system_properties.use_plus.Assign(1); + shared_memory->system_properties.is_charging_joy_right.Assign( + battery_level.right.is_charging); shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1); } shared_memory->system_properties.use_directional_buttons.Assign(1); shared_memory->system_properties.is_vertical.Assign(1); shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; + if (controller.is_dual_left_connected && controller.is_dual_right_connected) { shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual; + shared_memory->fullkey_color.fullkey = body_colors.left; + shared_memory->battery_level_dual = battery_level.left.battery_level; + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.left.is_charging); } else if (controller.is_dual_left_connected) { shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly; + shared_memory->fullkey_color.fullkey = body_colors.left; + shared_memory->battery_level_dual = battery_level.left.battery_level; + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.left.is_charging); } else { shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly; + shared_memory->fullkey_color.fullkey = body_colors.right; + shared_memory->battery_level_dual = battery_level.right.battery_level; + shared_memory->system_properties.is_charging_joy_dual.Assign( + battery_level.right.is_charging); } break; case Core::HID::NpadStyleIndex::JoyconLeft: + shared_memory->joycon_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.left = body_colors.left; + shared_memory->battery_level_dual = battery_level.left.battery_level; shared_memory->style_tag.joycon_left.Assign(1); shared_memory->device_type.joycon_left.Assign(1); shared_memory->system_properties.is_horizontal.Assign(1); shared_memory->system_properties.use_minus.Assign(1); + shared_memory->system_properties.is_charging_joy_left.Assign( + battery_level.left.is_charging); shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); break; case Core::HID::NpadStyleIndex::JoyconRight: + shared_memory->joycon_color.attribute = ColorAttribute::Ok; + shared_memory->joycon_color.right = body_colors.right; + shared_memory->battery_level_right = battery_level.right.battery_level; shared_memory->style_tag.joycon_right.Assign(1); shared_memory->device_type.joycon_right.Assign(1); shared_memory->system_properties.is_horizontal.Assign(1); shared_memory->system_properties.use_plus.Assign(1); + shared_memory->system_properties.is_charging_joy_right.Assign( + battery_level.right.is_charging); shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1); break; @@ -269,21 +331,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { break; } - const auto& body_colors = controller.device->GetColors(); - - shared_memory->fullkey_color.attribute = ColorAttribute::Ok; - shared_memory->fullkey_color.fullkey = body_colors.fullkey; - - shared_memory->joycon_color.attribute = ColorAttribute::Ok; - shared_memory->joycon_color.left = body_colors.left; - shared_memory->joycon_color.right = body_colors.right; - - // TODO: Investigate when we should report all batery types - const auto& battery_level = controller.device->GetBattery(); - shared_memory->battery_level_dual = battery_level.dual.battery_level; - shared_memory->battery_level_left = battery_level.left.battery_level; - shared_memory->battery_level_right = battery_level.right.battery_level; - controller.is_connected = true; controller.device->Connect(); SignalStyleSetChangedEvent(npad_id); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 5ecbddf94..3d3457160 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -819,12 +819,12 @@ void Hid::EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx const auto result = controller.EnableSixAxisSensorUnalteredPassthrough( parameters.sixaxis_handle, parameters.enabled); - LOG_WARNING(Service_HID, - "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " - "applet_resource_user_id={}", - parameters.enabled, parameters.sixaxis_handle.npad_type, - parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, - parameters.applet_resource_user_id); + LOG_DEBUG(Service_HID, + "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " + "applet_resource_user_id={}", + parameters.enabled, parameters.sixaxis_handle.npad_type, + parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); @@ -846,7 +846,7 @@ void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled( parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); - LOG_WARNING( + LOG_DEBUG( Service_HID, "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, @@ -2146,12 +2146,18 @@ public: {324, nullptr, "GetUniquePadButtonSet"}, {325, nullptr, "GetUniquePadColor"}, {326, nullptr, "GetUniquePadAppletDetailedUiType"}, + {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, + {328, nullptr, "AttachAbstractedPadToNpad"}, + {329, nullptr, "DetachAbstractedPadAll"}, + {330, nullptr, "CheckAbstractedPadConnection"}, {500, nullptr, "SetAppletResourceUserId"}, {501, nullptr, "RegisterAppletResourceUserId"}, {502, nullptr, "UnregisterAppletResourceUserId"}, {503, nullptr, "EnableAppletToGetInput"}, {504, nullptr, "SetAruidValidForVibration"}, {505, nullptr, "EnableAppletToGetSixAxisSensor"}, + {506, nullptr, "EnableAppletToGetPadInput"}, + {507, nullptr, "EnableAppletToGetTouchScreen"}, {510, nullptr, "SetVibrationMasterVolume"}, {511, nullptr, "GetVibrationMasterVolume"}, {512, nullptr, "BeginPermitVibrationSession"}, diff --git a/src/core/hle/service/ldn/errors.h b/src/core/hle/service/ldn/errors.h deleted file mode 100644 index 972a74806..000000000 --- a/src/core/hle/service/ldn/errors.h +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/result.h" - -namespace Service::LDN { - -constexpr Result ERROR_DISABLED{ErrorModule::LDN, 22}; - -} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 125d4dc4c..c11daff54 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -3,11 +3,15 @@ #include <memory> -#include "core/hle/ipc_helpers.h" -#include "core/hle/result.h" -#include "core/hle/service/ldn/errors.h" +#include "core/core.h" #include "core/hle/service/ldn/ldn.h" -#include "core/hle/service/sm/sm.h" +#include "core/hle/service/ldn/ldn_results.h" +#include "core/hle/service/ldn/ldn_types.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" + +// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent +#undef CreateEvent namespace Service::LDN { @@ -100,74 +104,418 @@ class IUserLocalCommunicationService final : public ServiceFramework<IUserLocalCommunicationService> { public: explicit IUserLocalCommunicationService(Core::System& system_) - : ServiceFramework{system_, "IUserLocalCommunicationService"} { + : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew}, + service_context{system, "IUserLocalCommunicationService"}, room_network{ + system_.GetRoomNetwork()} { // clang-format off static const FunctionInfo functions[] = { {0, &IUserLocalCommunicationService::GetState, "GetState"}, - {1, nullptr, "GetNetworkInfo"}, + {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, {2, nullptr, "GetIpv4Address"}, - {3, nullptr, "GetDisconnectReason"}, - {4, nullptr, "GetSecurityParameter"}, - {5, nullptr, "GetNetworkConfig"}, - {100, nullptr, "AttachStateChangeEvent"}, - {101, nullptr, "GetNetworkInfoLatestUpdate"}, - {102, nullptr, "Scan"}, - {103, nullptr, "ScanPrivate"}, + {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"}, + {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"}, + {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"}, + {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"}, + {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"}, + {102, &IUserLocalCommunicationService::Scan, "Scan"}, + {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"}, {104, nullptr, "SetWirelessControllerRestriction"}, - {200, nullptr, "OpenAccessPoint"}, - {201, nullptr, "CloseAccessPoint"}, - {202, nullptr, "CreateNetwork"}, - {203, nullptr, "CreateNetworkPrivate"}, - {204, nullptr, "DestroyNetwork"}, + {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, + {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, + {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, + {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"}, + {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"}, {205, nullptr, "Reject"}, - {206, nullptr, "SetAdvertiseData"}, - {207, nullptr, "SetStationAcceptPolicy"}, - {208, nullptr, "AddAcceptFilterEntry"}, + {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"}, + {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"}, + {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"}, {209, nullptr, "ClearAcceptFilter"}, - {300, nullptr, "OpenStation"}, - {301, nullptr, "CloseStation"}, - {302, nullptr, "Connect"}, + {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"}, + {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"}, + {302, &IUserLocalCommunicationService::Connect, "Connect"}, {303, nullptr, "ConnectPrivate"}, - {304, nullptr, "Disconnect"}, - {400, nullptr, "Initialize"}, - {401, nullptr, "Finalize"}, - {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+ + {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"}, + {400, &IUserLocalCommunicationService::Initialize, "Initialize"}, + {401, &IUserLocalCommunicationService::Finalize, "Finalize"}, + {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, }; // clang-format on RegisterHandlers(functions); + + state_change_event = + service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent"); + } + + ~IUserLocalCommunicationService() { + service_context.CloseEvent(state_change_event); + } + + void OnEventFired() { + state_change_event->GetWritableEvent().Signal(); } void GetState(Kernel::HLERequestContext& ctx) { + State state = State::Error; + LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(state); + } + + void GetNetworkInfo(Kernel::HLERequestContext& ctx) { + const auto write_buffer_size = ctx.GetWriteBufferSize(); + + if (write_buffer_size != sizeof(NetworkInfo)) { + LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + NetworkInfo network_info{}; + const auto rc = ResultSuccess; + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}", + network_info.common.ssid.GetStringValue(), network_info.ldn.node_count); + + ctx.WriteBuffer<NetworkInfo>(network_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + } + + void GetDisconnectReason(Kernel::HLERequestContext& ctx) { + const auto disconnect_reason = DisconnectReason::None; + + LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(disconnect_reason); + } + + void GetSecurityParameter(Kernel::HLERequestContext& ctx) { + SecurityParameter security_parameter{}; + NetworkInfo info{}; + const Result rc = ResultSuccess; + + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + security_parameter.session_id = info.network_id.session_id; + std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(), + sizeof(SecurityParameter::data)); + LOG_WARNING(Service_LDN, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(rc); + rb.PushRaw<SecurityParameter>(security_parameter); + } + + void GetNetworkConfig(Kernel::HLERequestContext& ctx) { + NetworkConfig config{}; + NetworkInfo info{}; + const Result rc = ResultSuccess; + + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + config.intent_id = info.network_id.intent_id; + config.channel = info.common.channel; + config.node_count_max = info.ldn.node_count_max; + config.local_communication_version = info.ldn.nodes[0].local_communication_version; + + LOG_WARNING(Service_LDN, + "(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, " + "local_communication_version={}", + config.intent_id.local_communication_id, config.intent_id.scene_id, + config.channel, config.node_count_max, config.local_communication_version); + + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(rc); + rb.PushRaw<NetworkConfig>(config); + } + + void AttachStateChangeEvent(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_LDN, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(state_change_event->GetReadableEvent()); + } + + void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) { + const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0); + const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); + + if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { + LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size, + node_buffer_count); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + NetworkInfo info; + std::vector<NodeLatestUpdate> latest_update(node_buffer_count); + + const auto rc = ResultSuccess; + if (rc.IsError()) { + LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + return; + } + + LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}", + info.common.ssid.GetStringValue(), info.ldn.node_count); + + ctx.WriteBuffer(info, 0); + ctx.WriteBuffer(latest_update, 1); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void Scan(Kernel::HLERequestContext& ctx) { + ScanImpl(ctx); + } + + void ScanPrivate(Kernel::HLERequestContext& ctx) { + ScanImpl(ctx, true); + } + + void ScanImpl(Kernel::HLERequestContext& ctx, bool is_private = false) { + IPC::RequestParser rp{ctx}; + const auto channel{rp.PopEnum<WifiChannel>()}; + const auto scan_filter{rp.PopRaw<ScanFilter>()}; + + const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo); + + if (network_info_size == 0) { + LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + u16 count = 0; + std::vector<NetworkInfo> network_infos(network_info_size); + + LOG_WARNING(Service_LDN, + "(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}", + channel, scan_filter.flag, scan_filter.network_type); + + ctx.WriteBuffer(network_infos); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(count); + } + + void OpenAccessPoint(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void CloseAccessPoint(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void CreateNetwork(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + SecurityConfig security_config; + UserConfig user_config; + INSERT_PADDING_WORDS_NOINIT(1); + NetworkConfig network_config; + }; + static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_LDN, + "(STUBBED) called, passphrase_size={}, security_mode={}, " + "local_communication_version={}", + parameters.security_config.passphrase_size, + parameters.security_config.security_mode, + parameters.network_config.local_communication_version); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + SecurityConfig security_config; + SecurityParameter security_parameter; + UserConfig user_config; + NetworkConfig network_config; + }; + static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_LDN, + "(STUBBED) called, passphrase_size={}, security_mode={}, " + "local_communication_version={}", + parameters.security_config.passphrase_size, + parameters.security_config.security_mode, + parameters.network_config.local_communication_version); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void DestroyNetwork(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void SetAdvertiseData(Kernel::HLERequestContext& ctx) { + std::vector<u8> read_buffer = ctx.ReadBuffer(); + + LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } - // Indicate a network error, as we do not actually emulate LDN - rb.Push(static_cast<u32>(State::Error)); + void AddAcceptFilterEntry(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void OpenStation(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void CloseStation(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void Connect(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + SecurityConfig security_config; + UserConfig user_config; + u32 local_communication_version; + u32 option; + }; + static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_LDN, + "(STUBBED) called, passphrase_size={}, security_mode={}, " + "local_communication_version={}", + parameters.security_config.passphrase_size, + parameters.security_config.security_mode, + parameters.local_communication_version); + + const std::vector<u8> read_buffer = ctx.ReadBuffer(); + NetworkInfo network_info{}; + + if (read_buffer.size() != sizeof(NetworkInfo)) { + LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultBadInput); + return; + } + + std::memcpy(&network_info, read_buffer.data(), read_buffer.size()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void Disconnect(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + void Initialize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + const auto rc = InitializeImpl(ctx); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(rc); + } + + void Finalize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + is_initialized = false; + + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void Initialize2(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_LDN, "called"); + LOG_WARNING(Service_LDN, "(STUBBED) called"); - is_initialized = true; + const auto rc = InitializeImpl(ctx); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_DISABLED); + rb.Push(rc); + } + + Result InitializeImpl(Kernel::HLERequestContext& ctx) { + const auto network_interface = Network::GetSelectedNetworkInterface(); + if (!network_interface) { + LOG_ERROR(Service_LDN, "No network interface is set"); + return ResultAirplaneModeEnabled; + } + + is_initialized = true; + // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented + return ResultAirplaneModeEnabled; } -private: - enum class State { - None, - Initialized, - AccessPointOpened, - AccessPointCreated, - StationOpened, - StationConnected, - Error, - }; + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* state_change_event; + Network::RoomNetwork& room_network; bool is_initialized{}; }; @@ -273,7 +621,7 @@ public: LOG_WARNING(Service_LDN, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_DISABLED); + rb.Push(ResultDisabled); } }; diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h index a0031ac71..6afe2ea6f 100644 --- a/src/core/hle/service/ldn/ldn.h +++ b/src/core/hle/service/ldn/ldn.h @@ -3,6 +3,12 @@ #pragma once +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/result.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/sm/sm.h" + namespace Core { class System; } diff --git a/src/core/hle/service/ldn/ldn_results.h b/src/core/hle/service/ldn/ldn_results.h new file mode 100644 index 000000000..f340bda42 --- /dev/null +++ b/src/core/hle/service/ldn/ldn_results.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::LDN { + +constexpr Result ResultAdvertiseDataTooLarge{ErrorModule::LDN, 10}; +constexpr Result ResultAuthenticationFailed{ErrorModule::LDN, 20}; +constexpr Result ResultDisabled{ErrorModule::LDN, 22}; +constexpr Result ResultAirplaneModeEnabled{ErrorModule::LDN, 23}; +constexpr Result ResultInvalidNodeCount{ErrorModule::LDN, 30}; +constexpr Result ResultConnectionFailed{ErrorModule::LDN, 31}; +constexpr Result ResultBadState{ErrorModule::LDN, 32}; +constexpr Result ResultNoIpAddress{ErrorModule::LDN, 33}; +constexpr Result ResultInvalidBufferCount{ErrorModule::LDN, 50}; +constexpr Result ResultAccessPointConnectionFailed{ErrorModule::LDN, 65}; +constexpr Result ResultAuthenticationTimeout{ErrorModule::LDN, 66}; +constexpr Result ResultMaximumNodeCount{ErrorModule::LDN, 67}; +constexpr Result ResultBadInput{ErrorModule::LDN, 96}; +constexpr Result ResultLocalCommunicationIdNotFound{ErrorModule::LDN, 97}; +constexpr Result ResultLocalCommunicationVersionTooLow{ErrorModule::LDN, 113}; +constexpr Result ResultLocalCommunicationVersionTooHigh{ErrorModule::LDN, 114}; + +} // namespace Service::LDN diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h new file mode 100644 index 000000000..6231e936d --- /dev/null +++ b/src/core/hle/service/ldn/ldn_types.h @@ -0,0 +1,292 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <fmt/format.h> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "network/network.h" + +namespace Service::LDN { + +constexpr size_t SsidLengthMax = 32; +constexpr size_t AdvertiseDataSizeMax = 384; +constexpr size_t UserNameBytesMax = 32; +constexpr int NodeCountMax = 8; +constexpr int StationCountMax = NodeCountMax - 1; +constexpr size_t PassphraseLengthMax = 64; + +enum class SecurityMode : u16 { + All, + Retail, + Debug, +}; + +enum class NodeStateChange : u8 { + None, + Connect, + Disconnect, + DisconnectAndConnect, +}; + +enum class ScanFilterFlag : u32 { + None = 0, + LocalCommunicationId = 1 << 0, + SessionId = 1 << 1, + NetworkType = 1 << 2, + Ssid = 1 << 4, + SceneId = 1 << 5, + IntentId = LocalCommunicationId | SceneId, + NetworkId = IntentId | SessionId, +}; + +enum class NetworkType : u32 { + None, + General, + Ldn, + All, +}; + +enum class PackedNetworkType : u8 { + None, + General, + Ldn, + All, +}; + +enum class State : u32 { + None, + Initialized, + AccessPointOpened, + AccessPointCreated, + StationOpened, + StationConnected, + Error, +}; + +enum class DisconnectReason : s16 { + Unknown = -1, + None, + DisconnectedByUser, + DisconnectedBySystem, + DestroyedByUser, + DestroyedBySystem, + Rejected, + SignalLost, +}; + +enum class NetworkError { + Unknown = -1, + None = 0, + PortUnreachable, + TooManyPlayers, + VersionTooLow, + VersionTooHigh, + ConnectFailure, + ConnectNotFound, + ConnectTimeout, + ConnectRejected, + RejectFailed, +}; + +enum class AcceptPolicy : u8 { + AcceptAll, + RejectAll, + BlackList, + WhiteList, +}; + +enum class WifiChannel : s16 { + Default = 0, + wifi24_1 = 1, + wifi24_6 = 6, + wifi24_11 = 11, + wifi50_36 = 36, + wifi50_40 = 40, + wifi50_44 = 44, + wifi50_48 = 48, +}; + +enum class LinkLevel : s8 { + Bad, + Low, + Good, + Excellent, +}; + +struct NodeLatestUpdate { + NodeStateChange state_change; + INSERT_PADDING_BYTES(0x7); // Unknown +}; +static_assert(sizeof(NodeLatestUpdate) == 0x8, "NodeLatestUpdate is an invalid size"); + +struct SessionId { + u64 high; + u64 low; + + bool operator==(const SessionId&) const = default; +}; +static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size"); + +struct IntentId { + u64 local_communication_id; + INSERT_PADDING_BYTES(0x2); // Reserved + u16 scene_id; + INSERT_PADDING_BYTES(0x4); // Reserved +}; +static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size"); + +struct NetworkId { + IntentId intent_id; + SessionId session_id; +}; +static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size"); + +struct Ssid { + u8 length{}; + std::array<char, SsidLengthMax + 1> raw{}; + + Ssid() = default; + + explicit Ssid(std::string_view data) { + length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); + data.copy(raw.data(), length); + raw[length] = 0; + } + + std::string GetStringValue() const { + return std::string(raw.data()); + } +}; +static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size"); + +struct Ipv4Address { + union { + u32 raw{}; + std::array<u8, 4> bytes; + }; + + std::string GetStringValue() const { + return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); + } +}; +static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size"); + +struct MacAddress { + std::array<u8, 6> raw{}; + + friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default; +}; +static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size"); + +struct ScanFilter { + NetworkId network_id; + NetworkType network_type; + MacAddress mac_address; + Ssid ssid; + INSERT_PADDING_BYTES(0x10); + ScanFilterFlag flag; +}; +static_assert(sizeof(ScanFilter) == 0x60, "ScanFilter is an invalid size"); + +struct CommonNetworkInfo { + MacAddress bssid; + Ssid ssid; + WifiChannel channel; + LinkLevel link_level; + PackedNetworkType network_type; + INSERT_PADDING_BYTES(0x4); +}; +static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size"); + +struct NodeInfo { + Ipv4Address ipv4_address; + MacAddress mac_address; + s8 node_id; + u8 is_connected; + std::array<u8, UserNameBytesMax + 1> user_name; + INSERT_PADDING_BYTES(0x1); // Reserved + s16 local_communication_version; + INSERT_PADDING_BYTES(0x10); // Reserved +}; +static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size"); + +struct LdnNetworkInfo { + std::array<u8, 0x10> security_parameter; + SecurityMode security_mode; + AcceptPolicy station_accept_policy; + u8 has_action_frame; + INSERT_PADDING_BYTES(0x2); // Padding + u8 node_count_max; + u8 node_count; + std::array<NodeInfo, NodeCountMax> nodes; + INSERT_PADDING_BYTES(0x2); // Reserved + u16 advertise_data_size; + std::array<u8, AdvertiseDataSizeMax> advertise_data; + INSERT_PADDING_BYTES(0x8C); // Reserved + u64 random_authentication_id; +}; +static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size"); + +struct NetworkInfo { + NetworkId network_id; + CommonNetworkInfo common; + LdnNetworkInfo ldn; +}; +static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size"); + +struct SecurityConfig { + SecurityMode security_mode; + u16 passphrase_size; + std::array<u8, PassphraseLengthMax> passphrase; +}; +static_assert(sizeof(SecurityConfig) == 0x44, "SecurityConfig is an invalid size"); + +struct UserConfig { + std::array<u8, UserNameBytesMax + 1> user_name; + INSERT_PADDING_BYTES(0xF); // Reserved +}; +static_assert(sizeof(UserConfig) == 0x30, "UserConfig is an invalid size"); + +#pragma pack(push, 4) +struct ConnectRequest { + SecurityConfig security_config; + UserConfig user_config; + u32 local_communication_version; + u32 option_unknown; + NetworkInfo network_info; +}; +static_assert(sizeof(ConnectRequest) == 0x4FC, "ConnectRequest is an invalid size"); +#pragma pack(pop) + +struct SecurityParameter { + std::array<u8, 0x10> data; // Data, used with the same key derivation as SecurityConfig + SessionId session_id; +}; +static_assert(sizeof(SecurityParameter) == 0x20, "SecurityParameter is an invalid size"); + +struct NetworkConfig { + IntentId intent_id; + WifiChannel channel; + u8 node_count_max; + INSERT_PADDING_BYTES(0x1); // Reserved + u16 local_communication_version; + INSERT_PADDING_BYTES(0xA); // Reserved +}; +static_assert(sizeof(NetworkConfig) == 0x20, "NetworkConfig is an invalid size"); + +struct AddressEntry { + Ipv4Address ipv4_address; + MacAddress mac_address; + INSERT_PADDING_BYTES(0x2); // Reserved +}; +static_assert(sizeof(AddressEntry) == 0xC, "AddressEntry is an invalid size"); + +struct AddressList { + std::array<AddressEntry, 0x8> addresses; +}; +static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size"); + +} // namespace Service::LDN diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp index 5ebb124a7..ba8c0e230 100644 --- a/src/core/hle/service/mm/mm_u.cpp +++ b/src/core/hle/service/mm/mm_u.cpp @@ -46,7 +46,7 @@ private: IPC::RequestParser rp{ctx}; min = rp.Pop<u32>(); max = rp.Pop<u32>(); - LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); + LOG_DEBUG(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); current = min; IPC::ResponseBuilder rb{ctx, 2}; @@ -54,7 +54,7 @@ private: } void GetOld(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_MM, "(STUBBED) called"); + LOG_DEBUG(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -81,8 +81,8 @@ private: u32 input_id = rp.Pop<u32>(); min = rp.Pop<u32>(); max = rp.Pop<u32>(); - LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", - input_id, min, max); + LOG_DEBUG(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", input_id, + min, max); current = min; IPC::ResponseBuilder rb{ctx, 2}; @@ -90,7 +90,7 @@ private: } void Get(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_MM, "(STUBBED) called"); + LOG_DEBUG(Service_MM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 2889973e4..e3ef06481 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -6,7 +6,6 @@ #include "core/hle/kernel/k_event.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nifm/nifm.h" -#include "core/hle/service/service.h" namespace { @@ -271,213 +270,228 @@ public: } }; -class IGeneralService final : public ServiceFramework<IGeneralService> { -public: - explicit IGeneralService(Core::System& system_); +void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) { + static constexpr u32 client_id = 1; + LOG_WARNING(Service_NIFM, "(STUBBED) called"); -private: - void GetClientId(Kernel::HLERequestContext& ctx) { - static constexpr u32 client_id = 1; - LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid +} - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid - } +void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); - void CreateScanRequest(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NIFM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IScanRequest>(system); +} - rb.Push(ResultSuccess); - rb.PushIpcInterface<IScanRequest>(system); - } +void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); - void CreateRequest(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NIFM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IRequest>(system); +} - rb.Push(ResultSuccess); - rb.PushIpcInterface<IRequest>(system); - } +void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); - void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); + const auto net_iface = Network::GetSelectedNetworkInterface(); - const auto net_iface = Network::GetSelectedNetworkInterface(); - - const SfNetworkProfileData network_profile_data = [&net_iface] { - if (!net_iface) { - return SfNetworkProfileData{}; - } - - return SfNetworkProfileData{ - .ip_setting_data{ - .ip_address_setting{ - .is_automatic{true}, - .current_address{Network::TranslateIPv4(net_iface->ip_address)}, - .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, - .gateway{Network::TranslateIPv4(net_iface->gateway)}, - }, - .dns_setting{ - .is_automatic{true}, - .primary_dns{1, 1, 1, 1}, - .secondary_dns{1, 0, 0, 1}, - }, - .proxy_setting{ - .enabled{false}, - .port{}, - .proxy_server{}, - .automatic_auth_enabled{}, - .user{}, - .password{}, - }, - .mtu{1500}, + SfNetworkProfileData network_profile_data = [&net_iface] { + if (!net_iface) { + return SfNetworkProfileData{}; + } + + return SfNetworkProfileData{ + .ip_setting_data{ + .ip_address_setting{ + .is_automatic{true}, + .current_address{Network::TranslateIPv4(net_iface->ip_address)}, + .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, + .gateway{Network::TranslateIPv4(net_iface->gateway)}, }, - .uuid{0xdeadbeef, 0xdeadbeef}, - .network_name{"yuzu Network"}, - .wireless_setting_data{ - .ssid_length{12}, - .ssid{"yuzu Network"}, - .passphrase{"yuzupassword"}, + .dns_setting{ + .is_automatic{true}, + .primary_dns{1, 1, 1, 1}, + .secondary_dns{1, 0, 0, 1}, }, - }; - }(); - - ctx.WriteBuffer(network_profile_data); + .proxy_setting{ + .enabled{false}, + .port{}, + .proxy_server{}, + .automatic_auth_enabled{}, + .user{}, + .password{}, + }, + .mtu{1500}, + }, + .uuid{0xdeadbeef, 0xdeadbeef}, + .network_name{"yuzu Network"}, + .wireless_setting_data{ + .ssid_length{12}, + .ssid{"yuzu Network"}, + .passphrase{"yuzupassword"}, + }, + }; + }(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + network_profile_data.ip_setting_data.ip_address_setting.current_address = + room_member->GetFakeIpAddress(); + } } - void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); + ctx.WriteBuffer(network_profile_data); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} - void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); +void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); - auto ipv4 = Network::GetHostIPv4Address(); - if (!ipv4) { - LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); - ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); - } + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw(*ipv4); +void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + auto ipv4 = Network::GetHostIPv4Address(); + if (!ipv4) { + LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); + ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); } - void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NIFM, "called"); + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + ipv4 = room_member->GetFakeIpAddress(); + } + } - ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, - "SfNetworkProfileData is not the correct size"); - u128 uuid{}; - auto buffer = ctx.ReadBuffer(); - std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(*ipv4); +} - IPC::ResponseBuilder rb{ctx, 6, 0, 1}; +void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); - rb.Push(ResultSuccess); - rb.PushIpcInterface<INetworkProfile>(system); - rb.PushRaw<u128>(uuid); - } + ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size"); + u128 uuid{}; + auto buffer = ctx.ReadBuffer(); + std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); - void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 6, 0, 1}; - struct IpConfigInfo { - IpAddressSetting ip_address_setting{}; - DnsSetting dns_setting{}; - }; - static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), - "IpConfigInfo has incorrect size."); + rb.Push(ResultSuccess); + rb.PushIpcInterface<INetworkProfile>(system); + rb.PushRaw<u128>(uuid); +} - const auto net_iface = Network::GetSelectedNetworkInterface(); +void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); - const IpConfigInfo ip_config_info = [&net_iface] { - if (!net_iface) { - return IpConfigInfo{}; - } + struct IpConfigInfo { + IpAddressSetting ip_address_setting{}; + DnsSetting dns_setting{}; + }; + static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), + "IpConfigInfo has incorrect size."); - return IpConfigInfo{ - .ip_address_setting{ - .is_automatic{true}, - .current_address{Network::TranslateIPv4(net_iface->ip_address)}, - .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, - .gateway{Network::TranslateIPv4(net_iface->gateway)}, - }, - .dns_setting{ - .is_automatic{true}, - .primary_dns{1, 1, 1, 1}, - .secondary_dns{1, 0, 0, 1}, - }, - }; - }(); + const auto net_iface = Network::GetSelectedNetworkInterface(); - IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; - rb.Push(ResultSuccess); - rb.PushRaw<IpConfigInfo>(ip_config_info); - } + IpConfigInfo ip_config_info = [&net_iface] { + if (!net_iface) { + return IpConfigInfo{}; + } - void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); + return IpConfigInfo{ + .ip_address_setting{ + .is_automatic{true}, + .current_address{Network::TranslateIPv4(net_iface->ip_address)}, + .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)}, + .gateway{Network::TranslateIPv4(net_iface->gateway)}, + }, + .dns_setting{ + .is_automatic{true}, + .primary_dns{1, 1, 1, 1}, + .secondary_dns{1, 0, 0, 1}, + }, + }; + }(); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u8>(0); + // When we're connected to a room, spoof the hosts IP address + if (auto room_member = network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress(); + } } - void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; + rb.Push(ResultSuccess); + rb.PushRaw<IpConfigInfo>(ip_config_info); +} - struct Output { - InternetConnectionType type{InternetConnectionType::WiFi}; - u8 wifi_strength{3}; - InternetConnectionStatus state{InternetConnectionStatus::Connected}; - }; - static_assert(sizeof(Output) == 0x3, "Output has incorrect size."); +void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); - constexpr Output out{}; + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u8>(1); +} - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw(out); - } +void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); - void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); + struct Output { + InternetConnectionType type{InternetConnectionType::WiFi}; + u8 wifi_strength{3}; + InternetConnectionStatus state{InternetConnectionStatus::Connected}; + }; + static_assert(sizeof(Output) == 0x3, "Output has incorrect size."); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - if (Network::GetHostIPv4Address().has_value()) { - rb.Push<u8>(1); - } else { - rb.Push<u8>(0); - } + constexpr Output out{}; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(out); +} + +void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + if (Network::GetHostIPv4Address().has_value()) { + rb.Push<u8>(1); + } else { + rb.Push<u8>(0); } +} - void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NIFM, "(STUBBED) called"); +void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { + LOG_ERROR(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - if (Network::GetHostIPv4Address().has_value()) { - rb.Push<u8>(1); - } else { - rb.Push<u8>(0); - } + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + if (Network::GetHostIPv4Address().has_value()) { + rb.Push<u8>(1); + } else { + rb.Push<u8>(0); } -}; +} IGeneralService::IGeneralService(Core::System& system_) - : ServiceFramework{system_, "IGeneralService"} { + : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} { // clang-format off static const FunctionInfo functions[] = { {1, &IGeneralService::GetClientId, "GetClientId"}, @@ -528,6 +542,8 @@ IGeneralService::IGeneralService(Core::System& system_) RegisterHandlers(functions); } +IGeneralService::~IGeneralService() = default; + class NetworkInterface final : public ServiceFramework<NetworkInterface> { public: explicit NetworkInterface(const char* name, Core::System& system_) diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index 5f62d0014..48161be28 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h @@ -3,6 +3,11 @@ #pragma once +#include "core/hle/service/service.h" +#include "network/network.h" +#include "network/room.h" +#include "network/room_member.h" + namespace Core { class System; } @@ -16,4 +21,26 @@ namespace Service::NIFM { /// Registers all NIFM services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); +class IGeneralService final : public ServiceFramework<IGeneralService> { +public: + explicit IGeneralService(Core::System& system_); + ~IGeneralService() override; + +private: + void GetClientId(Kernel::HLERequestContext& ctx); + void CreateScanRequest(Kernel::HLERequestContext& ctx); + void CreateRequest(Kernel::HLERequestContext& ctx); + void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx); + void RemoveNetworkProfile(Kernel::HLERequestContext& ctx); + void GetCurrentIpAddress(Kernel::HLERequestContext& ctx); + void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx); + void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx); + void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx); + void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx); + void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx); + void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx); + + Network::RoomNetwork& network; +}; + } // namespace Service::NIFM diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/iplatform_service_manager.cpp index cc11f3e08..fd047ff26 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/iplatform_service_manager.cpp @@ -20,7 +20,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_memory.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/ns/pl_u.h" +#include "core/hle/service/ns/iplatform_service_manager.h" namespace Service::NS { @@ -99,7 +99,7 @@ static u32 GetU32Swapped(const u8* data) { return Common::swap32(value); } -struct PL_U::Impl { +struct IPlatformServiceManager::Impl { const FontRegion& GetSharedFontRegion(std::size_t index) const { if (index >= shared_font_regions.size() || shared_font_regions.empty()) { // No font fallback @@ -134,16 +134,16 @@ struct PL_U::Impl { std::vector<FontRegion> shared_font_regions; }; -PL_U::PL_U(Core::System& system_) - : ServiceFramework{system_, "pl:u"}, impl{std::make_unique<Impl>()} { +IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_) + : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} { // clang-format off static const FunctionInfo functions[] = { - {0, &PL_U::RequestLoad, "RequestLoad"}, - {1, &PL_U::GetLoadState, "GetLoadState"}, - {2, &PL_U::GetSize, "GetSize"}, - {3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, - {4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, - {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, + {0, &IPlatformServiceManager::RequestLoad, "RequestLoad"}, + {1, &IPlatformServiceManager::GetLoadState, "GetLoadState"}, + {2, &IPlatformServiceManager::GetSize, "GetSize"}, + {3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, + {4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, + {5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"}, {100, nullptr, "RequestApplicationFunctionAuthorization"}, {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"}, @@ -206,9 +206,9 @@ PL_U::PL_U(Core::System& system_) } } -PL_U::~PL_U() = default; +IPlatformServiceManager::~IPlatformServiceManager() = default; -void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::RequestLoad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 shared_font_type{rp.Pop<u32>()}; // Games don't call this so all fonts should be loaded @@ -218,7 +218,7 @@ void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } -void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::GetLoadState(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; LOG_DEBUG(Service_NS, "called, font_id={}", font_id); @@ -228,7 +228,7 @@ void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { rb.Push<u32>(static_cast<u32>(LoadState::Done)); } -void PL_U::GetSize(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::GetSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; LOG_DEBUG(Service_NS, "called, font_id={}", font_id); @@ -238,7 +238,7 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) { rb.Push<u32>(impl->GetSharedFontRegion(font_id).size); } -void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; LOG_DEBUG(Service_NS, "called, font_id={}", font_id); @@ -248,7 +248,7 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset); } -void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { // Map backing memory for the font data LOG_DEBUG(Service_NS, "called"); @@ -261,7 +261,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { rb.PushCopyObjects(&kernel.GetFontSharedMem()); } -void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { +void IPlatformServiceManager::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code); diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/iplatform_service_manager.h index 07d0ac934..ed6eda89f 100644 --- a/src/core/hle/service/ns/pl_u.h +++ b/src/core/hle/service/ns/iplatform_service_manager.h @@ -36,10 +36,10 @@ constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{ void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output); void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset); -class PL_U final : public ServiceFramework<PL_U> { +class IPlatformServiceManager final : public ServiceFramework<IPlatformServiceManager> { public: - explicit PL_U(Core::System& system_); - ~PL_U() override; + explicit IPlatformServiceManager(Core::System& system_, const char* service_name_); + ~IPlatformServiceManager() override; private: void RequestLoad(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index aafc8fe03..f7318c3cb 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -9,10 +9,10 @@ #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/ns/errors.h" +#include "core/hle/service/ns/iplatform_service_manager.h" #include "core/hle/service/ns/language.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/pdm_qry.h" -#include "core/hle/service/ns/pl_u.h" #include "core/hle/service/set/set.h" namespace Service::NS { @@ -764,7 +764,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager); - std::make_shared<PL_U>(system)->InstallAsService(service_manager); + std::make_shared<IPlatformServiceManager>(system, "pl:s")->InstallAsService(service_manager); + std::make_shared<IPlatformServiceManager>(system, "pl:u")->InstallAsService(service_manager); } } // namespace Service::NS diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 2a5128c60..a7385fce8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "audio_core/audio_core.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" @@ -65,7 +66,10 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& return NvResult::NotImplemented; } -void nvhost_nvdec::OnOpen(DeviceFD fd) {} +void nvhost_nvdec::OnOpen(DeviceFD fd) { + LOG_INFO(Service_NVDRV, "NVDEC video stream started"); + system.AudioCore().SetNVDECActive(true); +} void nvhost_nvdec::OnClose(DeviceFD fd) { LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); @@ -73,6 +77,7 @@ void nvhost_nvdec::OnClose(DeviceFD fd) { if (iter != fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } + system.AudioCore().SetNVDECActive(false); } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 728bfa00b..d8518149d 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -198,7 +198,7 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) IocParamParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); + LOG_DEBUG(Service_NVDRV, "(STUBBED) called type={}", params.param); auto object = GetObject(params.handle); if (!object) { @@ -243,7 +243,7 @@ NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { IocFreeParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); auto itr = handles.find(params.handle); if (itr == handles.end()) { diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp index 0989474be..f7a497a14 100644 --- a/src/core/hle/service/pcv/pcv.cpp +++ b/src/core/hle/service/pcv/pcv.cpp @@ -3,6 +3,7 @@ #include <memory> +#include "core/hle/ipc_helpers.h" #include "core/hle/service/pcv/pcv.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -77,10 +78,102 @@ public: } }; +class IClkrstSession final : public ServiceFramework<IClkrstSession> { +public: + explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_) + : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "SetClockEnabled"}, + {1, nullptr, "SetClockDisabled"}, + {2, nullptr, "SetResetAsserted"}, + {3, nullptr, "SetResetDeasserted"}, + {4, nullptr, "SetPowerEnabled"}, + {5, nullptr, "SetPowerDisabled"}, + {6, nullptr, "GetState"}, + {7, &IClkrstSession::SetClockRate, "SetClockRate"}, + {8, &IClkrstSession::GetClockRate, "GetClockRate"}, + {9, nullptr, "SetMinVClockRate"}, + {10, nullptr, "GetPossibleClockRates"}, + {11, nullptr, "GetDvfsTable"}, + }; + // clang-format on + RegisterHandlers(functions); + } + +private: + void SetClockRate(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + clock_rate = rp.Pop<u32>(); + LOG_DEBUG(Service_PCV, "(STUBBED) called, clock_rate={}", clock_rate); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetClockRate(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCV, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(clock_rate); + } + + DeviceCode deivce_code; + u32 clock_rate{}; +}; + +class CLKRST final : public ServiceFramework<CLKRST> { +public: + explicit CLKRST(Core::System& system_, const char* name) : ServiceFramework{system_, name} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &CLKRST::OpenSession, "OpenSession"}, + {1, nullptr, "GetTemperatureThresholds"}, + {2, nullptr, "SetTemperature"}, + {3, nullptr, "GetModuleStateTable"}, + {4, nullptr, "GetModuleStateTableEvent"}, + {5, nullptr, "GetModuleStateTableMaxCount"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void OpenSession(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>()); + const auto unkonwn_input = rp.Pop<u32>(); + + LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IClkrstSession>(system, device_code); + } +}; + +class CLKRST_A final : public ServiceFramework<CLKRST_A> { +public: + explicit CLKRST_A(Core::System& system_) : ServiceFramework{system_, "clkrst:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ReleaseControl"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { std::make_shared<PCV>(system)->InstallAsService(sm); std::make_shared<PCV_ARB>(system)->InstallAsService(sm); std::make_shared<PCV_IMM>(system)->InstallAsService(sm); + std::make_shared<CLKRST>(system, "clkrst")->InstallAsService(sm); + std::make_shared<CLKRST>(system, "clkrst:i")->InstallAsService(sm); + std::make_shared<CLKRST_A>(system)->InstallAsService(sm); } } // namespace Service::PCV diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h index a42e6f8f6..6b26b6fa7 100644 --- a/src/core/hle/service/pcv/pcv.h +++ b/src/core/hle/service/pcv/pcv.h @@ -13,6 +13,97 @@ class ServiceManager; namespace Service::PCV { +enum class DeviceCode : u32 { + Cpu = 0x40000001, + Gpu = 0x40000002, + I2s1 = 0x40000003, + I2s2 = 0x40000004, + I2s3 = 0x40000005, + Pwm = 0x40000006, + I2c1 = 0x02000001, + I2c2 = 0x02000002, + I2c3 = 0x02000003, + I2c4 = 0x02000004, + I2c5 = 0x02000005, + I2c6 = 0x02000006, + Spi1 = 0x07000000, + Spi2 = 0x07000001, + Spi3 = 0x07000002, + Spi4 = 0x07000003, + Disp1 = 0x40000011, + Disp2 = 0x40000012, + Isp = 0x40000013, + Vi = 0x40000014, + Sdmmc1 = 0x40000015, + Sdmmc2 = 0x40000016, + Sdmmc3 = 0x40000017, + Sdmmc4 = 0x40000018, + Owr = 0x40000019, + Csite = 0x4000001A, + Tsec = 0x4000001B, + Mselect = 0x4000001C, + Hda2codec2x = 0x4000001D, + Actmon = 0x4000001E, + I2cSlow = 0x4000001F, + Sor1 = 0x40000020, + Sata = 0x40000021, + Hda = 0x40000022, + XusbCoreHostSrc = 0x40000023, + XusbFalconSrc = 0x40000024, + XusbFsSrc = 0x40000025, + XusbCoreDevSrc = 0x40000026, + XusbSsSrc = 0x40000027, + UartA = 0x03000001, + UartB = 0x35000405, + UartC = 0x3500040F, + UartD = 0x37000001, + Host1x = 0x4000002C, + Entropy = 0x4000002D, + SocTherm = 0x4000002E, + Vic = 0x4000002F, + Nvenc = 0x40000030, + Nvjpg = 0x40000031, + Nvdec = 0x40000032, + Qspi = 0x40000033, + ViI2c = 0x40000034, + Tsecb = 0x40000035, + Ape = 0x40000036, + AudioDsp = 0x40000037, + AudioUart = 0x40000038, + Emc = 0x40000039, + Plle = 0x4000003A, + PlleHwSeq = 0x4000003B, + Dsi = 0x4000003C, + Maud = 0x4000003D, + Dpaux1 = 0x4000003E, + MipiCal = 0x4000003F, + UartFstMipiCal = 0x40000040, + Osc = 0x40000041, + SysBus = 0x40000042, + SorSafe = 0x40000043, + XusbSs = 0x40000044, + XusbHost = 0x40000045, + XusbDevice = 0x40000046, + Extperiph1 = 0x40000047, + Ahub = 0x40000048, + Hda2hdmicodec = 0x40000049, + Gpuaux = 0x4000004A, + UsbD = 0x4000004B, + Usb2 = 0x4000004C, + Pcie = 0x4000004D, + Afi = 0x4000004E, + PciExClk = 0x4000004F, + PExUsbPhy = 0x40000050, + XUsbPadCtl = 0x40000051, + Apbdma = 0x40000052, + Usb2TrkClk = 0x40000053, + XUsbIoPll = 0x40000054, + XUsbIoPllHwSeq = 0x40000055, + Cec = 0x40000056, + Extperiph2 = 0x40000057, + OscClk = 0x40000080 +}; + void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); } // namespace Service::PCV diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index c7194731e..cc679cc81 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -9,12 +9,16 @@ #include <fmt/format.h> #include "common/microprofile.h" +#include "common/socket_types.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/service/sockets/bsd.h" #include "core/hle/service/sockets/sockets_translate.h" #include "core/internal_network/network.h" +#include "core/internal_network/socket_proxy.h" #include "core/internal_network/sockets.h" +#include "network/network.h" namespace Service::Sockets { @@ -472,7 +476,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco LOG_INFO(Service, "New socket fd={}", fd); - descriptor.socket = std::make_unique<Network::Socket>(); + auto room_member = room_network.GetRoomMember().lock(); + if (room_member && room_member->IsConnected()) { + descriptor.socket = std::make_unique<Network::ProxySocket>(room_network); + } else { + descriptor.socket = std::make_unique<Network::Socket>(); + } + descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); descriptor.is_connection_based = IsConnectionBased(type); @@ -648,7 +658,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { ASSERT(arg == 0); return {descriptor.flags, Errno::SUCCESS}; case FcntlCmd::SETFL: { - const bool enable = (arg & FLAG_O_NONBLOCK) != 0; + const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0; const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); if (bsd_errno != Errno::SUCCESS) { return {-1, bsd_errno}; @@ -669,7 +679,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con return Errno::BADF; } - Network::Socket* const socket = file_descriptors[fd]->socket.get(); + Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); if (optname == OptName::LINGER) { ASSERT(optlen == sizeof(Linger)); @@ -724,6 +734,8 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) FileDescriptor& descriptor = *file_descriptors[fd]; // Apply flags + using Network::FLAG_MSG_DONTWAIT; + using Network::FLAG_O_NONBLOCK; if ((flags & FLAG_MSG_DONTWAIT) != 0) { flags &= ~FLAG_MSG_DONTWAIT; if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { @@ -759,6 +771,8 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess } // Apply flags + using Network::FLAG_MSG_DONTWAIT; + using Network::FLAG_O_NONBLOCK; if ((flags & FLAG_MSG_DONTWAIT) != 0) { flags &= ~FLAG_MSG_DONTWAIT; if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { @@ -857,8 +871,19 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co rb.PushEnum(bsd_errno); } +void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) { + for (auto& optional_descriptor : file_descriptors) { + if (!optional_descriptor.has_value()) { + continue; + } + FileDescriptor& descriptor = *optional_descriptor; + descriptor.socket.get()->HandleProxyPacket(packet); + } +} + BSD::BSD(Core::System& system_, const char* name) - : ServiceFramework{system_, name, ServiceThreadType::CreateNew} { + : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, room_network{ + system_.GetRoomNetwork()} { // clang-format off static const FunctionInfo functions[] = { {0, &BSD::RegisterClient, "RegisterClient"}, @@ -899,9 +924,20 @@ BSD::BSD(Core::System& system_, const char* name) // clang-format on RegisterHandlers(functions); + + if (auto room_member = room_network.GetRoomMember().lock()) { + proxy_packet_received = room_member->BindOnProxyPacketReceived( + [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); }); + } else { + LOG_ERROR(Service, "Network isn't initalized"); + } } -BSD::~BSD() = default; +BSD::~BSD() { + if (auto room_member = room_network.GetRoomMember().lock()) { + room_member->Unbind(proxy_packet_received); + } +} BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} { // clang-format off diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 9ea36428d..81e855e0f 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -7,14 +7,17 @@ #include <vector> #include "common/common_types.h" +#include "common/socket_types.h" #include "core/hle/service/service.h" #include "core/hle/service/sockets/sockets.h" +#include "network/network.h" namespace Core { class System; } namespace Network { +class SocketBase; class Socket; } // namespace Network @@ -30,7 +33,7 @@ private: static constexpr size_t MAX_FD = 128; struct FileDescriptor { - std::unique_ptr<Network::Socket> socket; + std::unique_ptr<Network::SocketBase> socket; s32 flags = 0; bool is_connection_based = false; }; @@ -165,6 +168,14 @@ private: void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; + + Network::RoomNetwork& room_network; + + /// Callback to parse and handle a received wifi packet. + void OnProxyPacketReceived(const Network::ProxyPacket& packet); + + // Callback identifier for the OnProxyPacketReceived event. + Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received; }; class BSDCFG final : public ServiceFramework<BSDCFG> { diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index b735b00fc..31b7dad33 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -22,7 +22,9 @@ enum class Errno : u32 { AGAIN = 11, INVAL = 22, MFILE = 24, + MSGSIZE = 90, NOTCONN = 107, + TIMEDOUT = 110, }; enum class Domain : u32 { @@ -96,10 +98,6 @@ struct Linger { u32 linger; }; -constexpr u32 FLAG_MSG_DONTWAIT = 0x80; - -constexpr u32 FLAG_O_NONBLOCK = 0x800; - /// Registers all Sockets services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index 2db10ec81..023aa0486 100644 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp @@ -25,6 +25,8 @@ Errno Translate(Network::Errno value) { return Errno::MFILE; case Network::Errno::NOTCONN: return Errno::NOTCONN; + case Network::Errno::TIMEDOUT: + return Errno::TIMEDOUT; default: UNIMPLEMENTED_MSG("Unimplemented errno={}", value); return Errno::SUCCESS; diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index 36c43cc8f..cdf38a2a4 100644 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp @@ -32,6 +32,7 @@ #include "core/internal_network/network.h" #include "core/internal_network/network_interface.h" #include "core/internal_network/sockets.h" +#include "network/network.h" namespace Network { @@ -114,7 +115,10 @@ Errno TranslateNativeError(int e) { return Errno::NETDOWN; case WSAENETUNREACH: return Errno::NETUNREACH; + case WSAEMSGSIZE: + return Errno::MSGSIZE; default: + UNIMPLEMENTED_MSG("Unimplemented errno={}", e); return Errno::OTHER; } } @@ -125,7 +129,6 @@ using SOCKET = int; using WSAPOLLFD = pollfd; using ULONG = u64; -constexpr SOCKET INVALID_SOCKET = -1; constexpr SOCKET SOCKET_ERROR = -1; constexpr int SD_RECEIVE = SHUT_RD; @@ -206,7 +209,10 @@ Errno TranslateNativeError(int e) { return Errno::NETDOWN; case ENETUNREACH: return Errno::NETUNREACH; + case EMSGSIZE: + return Errno::MSGSIZE; default: + UNIMPLEMENTED_MSG("Unimplemented errno={}", e); return Errno::OTHER; } } @@ -329,16 +335,6 @@ PollEvents TranslatePollRevents(short revents) { return result; } -template <typename T> -Errno SetSockOpt(SOCKET fd, int option, T value) { - const int result = - setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); - if (result != SOCKET_ERROR) { - return Errno::SUCCESS; - } - return GetAndLogLastError(); -} - } // Anonymous namespace NetworkInstance::NetworkInstance() { @@ -350,26 +346,16 @@ NetworkInstance::~NetworkInstance() { } std::optional<IPv4Address> GetHostIPv4Address() { - const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); - const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); - if (network_interfaces.size() == 0) { - LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); + const auto network_interface = Network::GetSelectedNetworkInterface(); + if (!network_interface.has_value()) { + LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface"); return {}; } - const auto res = - std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { - return iface.name == selected_network_interface; - }); - - if (res != network_interfaces.end()) { - char ip_addr[16] = {}; - ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr); - return TranslateIPv4(res->ip_address); - } else { - LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); - return {}; - } + std::array<char, 16> ip_addr = {}; + ASSERT(inet_ntop(AF_INET, &network_interface->ip_address, ip_addr.data(), sizeof(ip_addr)) != + nullptr); + return TranslateIPv4(network_interface->ip_address); } std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { @@ -412,7 +398,19 @@ Socket::~Socket() { fd = INVALID_SOCKET; } -Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} +Socket::Socket(Socket&& rhs) noexcept { + fd = std::exchange(rhs.fd, INVALID_SOCKET); +} + +template <typename T> +Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) { + const int result = + setsockopt(fd_, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); + if (result != SOCKET_ERROR) { + return Errno::SUCCESS; + } + return GetAndLogLastError(); +} Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); @@ -423,7 +421,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { return GetAndLogLastError(); } -std::pair<Socket::AcceptResult, Errno> Socket::Accept() { +std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { sockaddr addr; socklen_t addrlen = sizeof(addr); const SOCKET new_socket = accept(fd, &addr, &addrlen); @@ -634,4 +632,8 @@ bool Socket::IsOpened() const { return fd != INVALID_SOCKET; } +void Socket::HandleProxyPacket(const ProxyPacket& packet) { + LOG_WARNING(Network, "ProxyPacket received, but not in Proxy mode!"); +} + } // namespace Network diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h index 10e5ef10d..36994c22e 100644 --- a/src/core/internal_network/network.h +++ b/src/core/internal_network/network.h @@ -8,6 +8,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/socket_types.h" #ifdef _WIN32 #include <winsock2.h> @@ -17,6 +18,7 @@ namespace Network { +class SocketBase; class Socket; /// Error code for network functions @@ -31,46 +33,11 @@ enum class Errno { HOSTUNREACH, NETDOWN, NETUNREACH, + TIMEDOUT, + MSGSIZE, OTHER, }; -/// Address families -enum class Domain { - INET, ///< Address family for IPv4 -}; - -/// Socket types -enum class Type { - STREAM, - DGRAM, - RAW, - SEQPACKET, -}; - -/// Protocol values for sockets -enum class Protocol { - ICMP, - TCP, - UDP, -}; - -/// Shutdown mode -enum class ShutdownHow { - RD, - WR, - RDWR, -}; - -/// Array of IPv4 address -using IPv4Address = std::array<u8, 4>; - -/// Cross-platform sockaddr structure -struct SockAddrIn { - Domain family; - IPv4Address ip; - u16 portno; -}; - /// Cross-platform poll fd structure enum class PollEvents : u16 { @@ -86,7 +53,7 @@ enum class PollEvents : u16 { DECLARE_ENUM_FLAG_OPERATORS(PollEvents); struct PollFD { - Socket* socket; + SocketBase* socket; PollEvents events; PollEvents revents; }; diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp new file mode 100644 index 000000000..0c746bd82 --- /dev/null +++ b/src/core/internal_network/socket_proxy.cpp @@ -0,0 +1,290 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> +#include <thread> + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/internal_network/network.h" +#include "core/internal_network/network_interface.h" +#include "core/internal_network/socket_proxy.h" + +namespace Network { + +ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {} + +ProxySocket::~ProxySocket() { + if (fd == INVALID_SOCKET) { + return; + } + fd = INVALID_SOCKET; +} + +void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { + if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno || + closed) { + return; + } + + if (!broadcast && packet.broadcast) { + LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode"); + return; + } + + std::lock_guard guard(packets_mutex); + received_packets.push(packet); +} + +template <typename T> +Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) { + LOG_DEBUG(Network, "(STUBBED) called"); + return Errno::SUCCESS; +} + +Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) { + protocol = socket_protocol; + SetSockOpt(fd, SO_TYPE, type); + + return Errno::SUCCESS; +} + +std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() { + LOG_WARNING(Network, "(STUBBED) called"); + return {AcceptResult{}, Errno::SUCCESS}; +} + +Errno ProxySocket::Connect(SockAddrIn addr_in) { + LOG_WARNING(Network, "(STUBBED) called"); + return Errno::SUCCESS; +} + +std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() { + LOG_WARNING(Network, "(STUBBED) called"); + return {SockAddrIn{}, Errno::SUCCESS}; +} + +std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() { + LOG_WARNING(Network, "(STUBBED) called"); + return {SockAddrIn{}, Errno::SUCCESS}; +} + +Errno ProxySocket::Bind(SockAddrIn addr) { + if (is_bound) { + LOG_WARNING(Network, "Rebinding Socket is unimplemented!"); + return Errno::SUCCESS; + } + local_endpoint = addr; + is_bound = true; + + return Errno::SUCCESS; +} + +Errno ProxySocket::Listen(s32 backlog) { + LOG_WARNING(Network, "(STUBBED) called"); + return Errno::SUCCESS; +} + +Errno ProxySocket::Shutdown(ShutdownHow how) { + LOG_WARNING(Network, "(STUBBED) called"); + return Errno::SUCCESS; +} + +std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) { + LOG_WARNING(Network, "(STUBBED) called"); + ASSERT(flags == 0); + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + + return {static_cast<s32>(0), Errno::SUCCESS}; +} + +std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { + ASSERT(flags == 0); + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + + // TODO (flTobi): Verify the timeout behavior and break when connection is lost + const auto timestamp = std::chrono::steady_clock::now(); + // When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a + // packet arrives. In order to prevent lost packets from hanging the emulation thread, we set + // the timeout to 5s instead + const auto timeout = receive_timeout == 0 ? 5000 : receive_timeout; + while (true) { + { + std::lock_guard guard(packets_mutex); + if (received_packets.size() > 0) { + return ReceivePacket(flags, message, addr, message.size()); + } + } + + if (!blocking) { + return {-1, Errno::AGAIN}; + } + + std::this_thread::yield(); + + const auto time_diff = std::chrono::steady_clock::now() - timestamp; + const auto time_diff_ms = + std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count(); + + if (time_diff_ms > timeout) { + return {-1, Errno::TIMEDOUT}; + } + } +} + +std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message, + SockAddrIn* addr, std::size_t max_length) { + ProxyPacket& packet = received_packets.front(); + if (addr) { + addr->family = Domain::INET; + addr->ip = packet.local_endpoint.ip; // The senders ip address + addr->portno = packet.local_endpoint.portno; // The senders port number + } + + bool peek = (flags & FLAG_MSG_PEEK) != 0; + std::size_t read_bytes; + if (packet.data.size() > max_length) { + read_bytes = max_length; + message.clear(); + std::copy(packet.data.begin(), packet.data.begin() + read_bytes, + std::back_inserter(message)); + message.resize(max_length); + + if (protocol == Protocol::UDP) { + if (!peek) { + received_packets.pop(); + } + return {-1, Errno::MSGSIZE}; + } else if (protocol == Protocol::TCP) { + std::vector<u8> numArray(packet.data.size() - max_length); + std::copy(packet.data.begin() + max_length, packet.data.end(), + std::back_inserter(numArray)); + packet.data = numArray; + } + } else { + read_bytes = packet.data.size(); + message.clear(); + std::copy(packet.data.begin(), packet.data.end(), std::back_inserter(message)); + message.resize(max_length); + if (!peek) { + received_packets.pop(); + } + } + + return {static_cast<u32>(read_bytes), Errno::SUCCESS}; +} + +std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) { + LOG_WARNING(Network, "(STUBBED) called"); + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + ASSERT(flags == 0); + + return {static_cast<s32>(0), Errno::SUCCESS}; +} + +void ProxySocket::SendPacket(ProxyPacket& packet) { + if (auto room_member = room_network.GetRoomMember().lock()) { + if (room_member->IsConnected()) { + room_member->SendProxyPacket(packet); + } + } +} + +std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message, + const SockAddrIn* addr) { + ASSERT(flags == 0); + + if (!is_bound) { + LOG_ERROR(Network, "ProxySocket is not bound!"); + return {static_cast<s32>(message.size()), Errno::SUCCESS}; + } + + if (auto room_member = room_network.GetRoomMember().lock()) { + if (!room_member->IsConnected()) { + return {static_cast<s32>(message.size()), Errno::SUCCESS}; + } + } + + ProxyPacket packet; + packet.local_endpoint = local_endpoint; + packet.remote_endpoint = *addr; + packet.protocol = protocol; + packet.broadcast = broadcast && packet.remote_endpoint.ip[3] == 255; + + auto& ip = local_endpoint.ip; + auto ipv4 = Network::GetHostIPv4Address(); + // If the ip is all zeroes (INADDR_ANY) or if it matches the hosts ip address, + // replace it with a "fake" routing address + if (std::all_of(ip.begin(), ip.end(), [](u8 i) { return i == 0; }) || (ipv4 && ipv4 == ip)) { + if (auto room_member = room_network.GetRoomMember().lock()) { + packet.local_endpoint.ip = room_member->GetFakeIpAddress(); + } + } + + packet.data.clear(); + std::copy(message.begin(), message.end(), std::back_inserter(packet.data)); + + SendPacket(packet); + + return {static_cast<s32>(message.size()), Errno::SUCCESS}; +} + +Errno ProxySocket::Close() { + fd = INVALID_SOCKET; + closed = true; + + return Errno::SUCCESS; +} + +Errno ProxySocket::SetLinger(bool enable, u32 linger) { + struct Linger { + u16 linger_enable; + u16 linger_time; + } values; + values.linger_enable = enable ? 1 : 0; + values.linger_time = static_cast<u16>(linger); + + return SetSockOpt(fd, SO_LINGER, values); +} + +Errno ProxySocket::SetReuseAddr(bool enable) { + return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); +} + +Errno ProxySocket::SetBroadcast(bool enable) { + broadcast = enable; + return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); +} + +Errno ProxySocket::SetSndBuf(u32 value) { + return SetSockOpt(fd, SO_SNDBUF, value); +} + +Errno ProxySocket::SetKeepAlive(bool enable) { + return Errno::SUCCESS; +} + +Errno ProxySocket::SetRcvBuf(u32 value) { + return SetSockOpt(fd, SO_RCVBUF, value); +} + +Errno ProxySocket::SetSndTimeo(u32 value) { + send_timeout = value; + return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value)); +} + +Errno ProxySocket::SetRcvTimeo(u32 value) { + receive_timeout = value; + return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value)); +} + +Errno ProxySocket::SetNonBlock(bool enable) { + blocking = !enable; + return Errno::SUCCESS; +} + +bool ProxySocket::IsOpened() const { + return fd != INVALID_SOCKET; +} + +} // namespace Network diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h new file mode 100644 index 000000000..f12b5f567 --- /dev/null +++ b/src/core/internal_network/socket_proxy.h @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> +#include <vector> +#include <queue> + +#include "common/common_funcs.h" +#include "core/internal_network/sockets.h" +#include "network/network.h" + +namespace Network { + +class ProxySocket : public SocketBase { +public: + YUZU_NON_COPYABLE(ProxySocket); + YUZU_NON_MOVEABLE(ProxySocket); + + explicit ProxySocket(RoomNetwork& room_network_) noexcept; + ~ProxySocket() override; + + void HandleProxyPacket(const ProxyPacket& packet) override; + + Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override; + + Errno Close() override; + + std::pair<AcceptResult, Errno> Accept() override; + + Errno Connect(SockAddrIn addr_in) override; + + std::pair<SockAddrIn, Errno> GetPeerName() override; + + std::pair<SockAddrIn, Errno> GetSockName() override; + + Errno Bind(SockAddrIn addr) override; + + Errno Listen(s32 backlog) override; + + Errno Shutdown(ShutdownHow how) override; + + std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; + + std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; + + std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr, + std::size_t max_length); + + std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; + + void SendPacket(ProxyPacket& packet); + + std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, + const SockAddrIn* addr) override; + + Errno SetLinger(bool enable, u32 linger) override; + + Errno SetReuseAddr(bool enable) override; + + Errno SetBroadcast(bool enable) override; + + Errno SetKeepAlive(bool enable) override; + + Errno SetSndBuf(u32 value) override; + + Errno SetRcvBuf(u32 value) override; + + Errno SetSndTimeo(u32 value) override; + + Errno SetRcvTimeo(u32 value) override; + + Errno SetNonBlock(bool enable) override; + + template <typename T> + Errno SetSockOpt(SOCKET fd, int option, T value); + + bool IsOpened() const override; + +private: + bool broadcast = false; + bool closed = false; + u32 send_timeout = 0; + u32 receive_timeout = 0; + bool is_bound = false; + SockAddrIn local_endpoint{}; + bool blocking = true; + std::queue<ProxyPacket> received_packets; + Protocol protocol; + + std::mutex packets_mutex; + + RoomNetwork& room_network; +}; + +} // namespace Network diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h index 77e27e928..a70429b19 100644 --- a/src/core/internal_network/sockets.h +++ b/src/core/internal_network/sockets.h @@ -14,20 +14,88 @@ #include "common/common_types.h" #include "core/internal_network/network.h" +#include "network/network.h" // TODO: C++20 Replace std::vector usages with std::span namespace Network { -class Socket { +class SocketBase { public: +#ifdef YUZU_UNIX + using SOCKET = int; + static constexpr SOCKET INVALID_SOCKET = -1; + static constexpr SOCKET SOCKET_ERROR = -1; +#endif + struct AcceptResult { - std::unique_ptr<Socket> socket; + std::unique_ptr<SocketBase> socket; SockAddrIn sockaddr_in; }; + virtual ~SocketBase() = default; + + virtual SocketBase& operator=(const SocketBase&) = delete; + + // Avoid closing sockets implicitly + virtual SocketBase& operator=(SocketBase&&) noexcept = delete; + + virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0; + + virtual Errno Close() = 0; + + virtual std::pair<AcceptResult, Errno> Accept() = 0; + + virtual Errno Connect(SockAddrIn addr_in) = 0; + + virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0; + + virtual std::pair<SockAddrIn, Errno> GetSockName() = 0; + + virtual Errno Bind(SockAddrIn addr) = 0; + + virtual Errno Listen(s32 backlog) = 0; + + virtual Errno Shutdown(ShutdownHow how) = 0; + + virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0; + + virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, + SockAddrIn* addr) = 0; + + virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0; + + virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, + const SockAddrIn* addr) = 0; + + virtual Errno SetLinger(bool enable, u32 linger) = 0; - explicit Socket() = default; - ~Socket(); + virtual Errno SetReuseAddr(bool enable) = 0; + + virtual Errno SetKeepAlive(bool enable) = 0; + + virtual Errno SetBroadcast(bool enable) = 0; + + virtual Errno SetSndBuf(u32 value) = 0; + + virtual Errno SetRcvBuf(u32 value) = 0; + + virtual Errno SetSndTimeo(u32 value) = 0; + + virtual Errno SetRcvTimeo(u32 value) = 0; + + virtual Errno SetNonBlock(bool enable) = 0; + + virtual bool IsOpened() const = 0; + + virtual void HandleProxyPacket(const ProxyPacket& packet) = 0; + + SOCKET fd = INVALID_SOCKET; +}; + +class Socket : public SocketBase { +public: + Socket() = default; + ~Socket() override; Socket(const Socket&) = delete; Socket& operator=(const Socket&) = delete; @@ -37,57 +105,57 @@ public: // Avoid closing sockets implicitly Socket& operator=(Socket&&) noexcept = delete; - Errno Initialize(Domain domain, Type type, Protocol protocol); + Errno Initialize(Domain domain, Type type, Protocol protocol) override; - Errno Close(); + Errno Close() override; - std::pair<AcceptResult, Errno> Accept(); + std::pair<AcceptResult, Errno> Accept() override; - Errno Connect(SockAddrIn addr_in); + Errno Connect(SockAddrIn addr_in) override; - std::pair<SockAddrIn, Errno> GetPeerName(); + std::pair<SockAddrIn, Errno> GetPeerName() override; - std::pair<SockAddrIn, Errno> GetSockName(); + std::pair<SockAddrIn, Errno> GetSockName() override; - Errno Bind(SockAddrIn addr); + Errno Bind(SockAddrIn addr) override; - Errno Listen(s32 backlog); + Errno Listen(s32 backlog) override; - Errno Shutdown(ShutdownHow how); + Errno Shutdown(ShutdownHow how) override; - std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message); + std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; - std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr); + std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; - std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags); + std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; - std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr); + std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, + const SockAddrIn* addr) override; - Errno SetLinger(bool enable, u32 linger); + Errno SetLinger(bool enable, u32 linger) override; - Errno SetReuseAddr(bool enable); + Errno SetReuseAddr(bool enable) override; - Errno SetKeepAlive(bool enable); + Errno SetKeepAlive(bool enable) override; - Errno SetBroadcast(bool enable); + Errno SetBroadcast(bool enable) override; - Errno SetSndBuf(u32 value); + Errno SetSndBuf(u32 value) override; - Errno SetRcvBuf(u32 value); + Errno SetRcvBuf(u32 value) override; - Errno SetSndTimeo(u32 value); + Errno SetSndTimeo(u32 value) override; - Errno SetRcvTimeo(u32 value); + Errno SetRcvTimeo(u32 value) override; - Errno SetNonBlock(bool enable); + Errno SetNonBlock(bool enable) override; - bool IsOpened() const; + template <typename T> + Errno SetSockOpt(SOCKET fd, int option, T value); -#if defined(_WIN32) - SOCKET fd = INVALID_SOCKET; -#elif YUZU_UNIX - int fd = -1; -#endif + bool IsOpened() const override; + + void HandleProxyPacket(const ProxyPacket& packet) override; }; std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index 9af46a0f7..d8a1bf82a 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp @@ -14,7 +14,7 @@ namespace Loader { namespace { constexpr u32 PageAlignSize(u32 size) { - return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); + return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); } } // Anonymous namespace diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 1b0bb0876..73d04d7ee 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -125,7 +125,7 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) { } static constexpr u32 PageAlignSize(u32 size) { - return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); + return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); } static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) { diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 8dd956fc6..4c3b3c655 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -45,7 +45,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, } constexpr u32 PageAlignSize(u32 size) { - return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); + return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); } } // Anonymous namespace diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 1b44280b5..34ad7cadd 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -36,10 +36,11 @@ struct Memory::Impl { } void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { - ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); - ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); + ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); + ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base); ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target); - MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); + MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target, + Common::PageType::Memory); if (Settings::IsFastmemEnabled()) { system.DeviceMemory().buffer.Map(base, target - DramMemoryMap::Base, size); @@ -47,9 +48,10 @@ struct Memory::Impl { } void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { - ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); - ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); - MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped); + ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); + ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base); + MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0, + Common::PageType::Unmapped); if (Settings::IsFastmemEnabled()) { system.DeviceMemory().buffer.Unmap(base, size); @@ -57,7 +59,7 @@ struct Memory::Impl { } [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const { - const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; + const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]}; if (!paddr) { return {}; @@ -67,7 +69,7 @@ struct Memory::Impl { } [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const { - const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; + const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]}; if (paddr == 0) { return {}; @@ -176,13 +178,14 @@ struct Memory::Impl { auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) { const auto& page_table = process.PageTable().PageTableImpl(); std::size_t remaining_size = size; - std::size_t page_index = addr >> PAGE_BITS; - std::size_t page_offset = addr & PAGE_MASK; + std::size_t page_index = addr >> YUZU_PAGEBITS; + std::size_t page_offset = addr & YUZU_PAGEMASK; while (remaining_size) { const std::size_t copy_amount = - std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); - const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); + std::min(static_cast<std::size_t>(YUZU_PAGESIZE) - page_offset, remaining_size); + const auto current_vaddr = + static_cast<VAddr>((page_index << YUZU_PAGEBITS) + page_offset); const auto [pointer, type] = page_table.pointers[page_index].PointerType(); switch (type) { @@ -192,7 +195,7 @@ struct Memory::Impl { } case Common::PageType::Memory: { DEBUG_ASSERT(pointer); - u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS); + u8* mem_ptr = pointer + page_offset + (page_index << YUZU_PAGEBITS); on_memory(copy_amount, mem_ptr); break; } @@ -339,10 +342,10 @@ struct Memory::Impl { // Iterate over a contiguous CPU address space, marking/unmarking the region. // The region is at a granularity of CPU pages. - const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; - for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { + const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1; + for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) { const Common::PageType page_type{ - current_page_table->pointers[vaddr >> PAGE_BITS].Type()}; + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()}; if (debug) { // Switch page type to debug if now debug switch (page_type) { @@ -354,7 +357,7 @@ struct Memory::Impl { // Page is already marked. break; case Common::PageType::Memory: - current_page_table->pointers[vaddr >> PAGE_BITS].Store( + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( nullptr, Common::PageType::DebugMemory); break; default: @@ -371,9 +374,9 @@ struct Memory::Impl { // Don't mess with already non-debug or rasterizer memory. break; case Common::PageType::DebugMemory: { - u8* const pointer{GetPointerFromDebugMemory(vaddr & ~PAGE_MASK)}; - current_page_table->pointers[vaddr >> PAGE_BITS].Store( - pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory); + u8* const pointer{GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK)}; + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( + pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory); break; } default: @@ -398,10 +401,10 @@ struct Memory::Impl { // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size // is different). This assumes the specified GPU address region is contiguous as well. - const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; - for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { + const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1; + for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) { const Common::PageType page_type{ - current_page_table->pointers[vaddr >> PAGE_BITS].Type()}; + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()}; if (cached) { // Switch page type to cached if now cached switch (page_type) { @@ -411,7 +414,7 @@ struct Memory::Impl { break; case Common::PageType::DebugMemory: case Common::PageType::Memory: - current_page_table->pointers[vaddr >> PAGE_BITS].Store( + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( nullptr, Common::PageType::RasterizerCachedMemory); break; case Common::PageType::RasterizerCachedMemory: @@ -434,16 +437,16 @@ struct Memory::Impl { // that this area is already unmarked as cached. break; case Common::PageType::RasterizerCachedMemory: { - u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)}; + u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK)}; if (pointer == nullptr) { // It's possible that this function has been called while updating the // pagetable after unmapping a VMA. In that case the underlying VMA will no // longer exist, and we should just leave the pagetable entry blank. - current_page_table->pointers[vaddr >> PAGE_BITS].Store( + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( nullptr, Common::PageType::Unmapped); } else { - current_page_table->pointers[vaddr >> PAGE_BITS].Store( - pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory); + current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( + pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory); } break; } @@ -465,8 +468,8 @@ struct Memory::Impl { */ void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, Common::PageType type) { - LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * PAGE_SIZE, - (base + size) * PAGE_SIZE); + LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * YUZU_PAGESIZE, + (base + size) * YUZU_PAGESIZE); // During boot, current_page_table might not be set yet, in which case we need not flush if (system.IsPoweredOn()) { @@ -474,7 +477,7 @@ struct Memory::Impl { for (u64 i = 0; i < size; i++) { const auto page = base + i; if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) { - gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE); + gpu.FlushAndInvalidateRegion(page << YUZU_PAGEBITS, YUZU_PAGESIZE); } } } @@ -485,7 +488,7 @@ struct Memory::Impl { if (!target) { ASSERT_MSG(type != Common::PageType::Memory, - "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE); + "Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE); while (base != end) { page_table.pointers[base].Store(nullptr, type); @@ -496,14 +499,14 @@ struct Memory::Impl { } else { while (base != end) { page_table.pointers[base].Store( - system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type); - page_table.backing_addr[base] = target - (base << PAGE_BITS); + system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type); + page_table.backing_addr[base] = target - (base << YUZU_PAGEBITS); ASSERT_MSG(page_table.pointers[base].Pointer(), "memory mapping base yield a nullptr within the table"); base += 1; - target += PAGE_SIZE; + target += YUZU_PAGESIZE; } } } @@ -518,7 +521,7 @@ struct Memory::Impl { } // Avoid adding any extra logic to this fast-path block - const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); + const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw(); if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { return &pointer[vaddr]; } @@ -657,7 +660,7 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { const Kernel::KProcess& process = *system.CurrentProcess(); const auto& page_table = process.PageTable().PageTableImpl(); - const size_t page = vaddr >> PAGE_BITS; + const size_t page = vaddr >> YUZU_PAGEBITS; if (page >= page_table.pointers.size()) { return false; } @@ -668,9 +671,9 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const { VAddr end = base + size; - VAddr page = Common::AlignDown(base, PAGE_SIZE); + VAddr page = Common::AlignDown(base, YUZU_PAGESIZE); - for (; page < end; page += PAGE_SIZE) { + for (; page < end; page += YUZU_PAGESIZE) { if (!IsValidVirtualAddress(page)) { return false; } diff --git a/src/core/memory.h b/src/core/memory.h index 2a21fbcfd..a11ff8766 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -27,9 +27,9 @@ namespace Core::Memory { * Page size used by the ARM architecture. This is the smallest granularity with which memory can * be mapped. */ -constexpr std::size_t PAGE_BITS = 12; -constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS; -constexpr u64 PAGE_MASK = PAGE_SIZE - 1; +constexpr std::size_t YUZU_PAGEBITS = 12; +constexpr u64 YUZU_PAGESIZE = 1ULL << YUZU_PAGEBITS; +constexpr u64 YUZU_PAGEMASK = YUZU_PAGESIZE - 1; /// Virtual user-space memory regions enum : VAddr { |