summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt48
-rw-r--r--src/core/announce_multiplayer_session.cpp164
-rw-r--r--src/core/announce_multiplayer_session.h98
-rw-r--r--src/core/arm/arm_interface.cpp4
-rw-r--r--src/core/arm/arm_interface.h18
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp24
-rw-r--r--src/core/arm/cpu_interrupt_handler.h39
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp61
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp44
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp36
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h7
-rw-r--r--src/core/core.cpp87
-rw-r--r--src/core/core.h34
-rw-r--r--src/core/core_timing.cpp151
-rw-r--r--src/core/core_timing.h25
-rw-r--r--src/core/cpu_manager.cpp129
-rw-r--r--src/core/cpu_manager.h10
-rw-r--r--src/core/debugger/debugger.cpp9
-rw-r--r--src/core/file_sys/errors.h5
-rw-r--r--src/core/file_sys/ips_layer.cpp7
-rw-r--r--src/core/file_sys/patch_manager.cpp14
-rw-r--r--src/core/frontend/applets/software_keyboard.h2
-rw-r--r--src/core/frontend/emu_window.cpp5
-rw-r--r--src/core/frontend/emu_window.h5
-rw-r--r--src/core/hid/emulated_controller.cpp89
-rw-r--r--src/core/hid/emulated_controller.h47
-rw-r--r--src/core/hid/hid_types.h12
-rw-r--r--src/core/hid/input_converter.cpp14
-rw-r--r--src/core/hid/input_converter.h8
-rw-r--r--src/core/hid/irs_types.h301
-rw-r--r--src/core/hle/ipc.h5
-rw-r--r--src/core/hle/ipc_helpers.h5
-rw-r--r--src/core/hle/kernel/global_scheduler_context.cpp7
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp38
-rw-r--r--src/core/hle/kernel/hle_ipc.h8
-rw-r--r--src/core/hle/kernel/k_client_port.cpp5
-rw-r--r--src/core/hle/kernel/k_client_port.h5
-rw-r--r--src/core/hle/kernel/k_code_memory.h6
-rw-r--r--src/core/hle/kernel/k_interrupt_manager.cpp7
-rw-r--r--src/core/hle/kernel/k_process.cpp5
-rw-r--r--src/core/hle/kernel/k_process.h5
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp735
-rw-r--r--src/core/hle/kernel/k_scheduler.h223
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h2
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp5
-rw-r--r--src/core/hle/kernel/k_shared_memory.h5
-rw-r--r--src/core/hle/kernel/k_thread.cpp42
-rw-r--r--src/core/hle/kernel/k_thread.h26
-rw-r--r--src/core/hle/kernel/kernel.cpp102
-rw-r--r--src/core/hle/kernel/kernel.h11
-rw-r--r--src/core/hle/kernel/physical_core.cpp30
-rw-r--r--src/core/hle/kernel/physical_core.h17
-rw-r--r--src/core/hle/kernel/svc.cpp7
-rw-r--r--src/core/hle/result.h5
-rw-r--r--src/core/hle/service/acc/acc.cpp20
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp11
-rw-r--r--src/core/hle/service/acc/profile_manager.h15
-rw-r--r--src/core/hle/service/am/am.cpp17
-rw-r--r--src/core/hle/service/am/am.h2
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp6
-rw-r--r--src/core/hle/service/audio/audin_u.cpp384
-rw-r--r--src/core/hle/service/audio/audin_u.h47
-rw-r--r--src/core/hle/service/audio/audout_u.cpp327
-rw-r--r--src/core/hle/service/audio/audout_u.h21
-rw-r--r--src/core/hle/service/audio/audren_u.cpp707
-rw-r--r--src/core/hle/service/audio/audren_u.h22
-rw-r--r--src/core/hle/service/audio/errors.h9
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/bcat/bcat_module.cpp4
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp5
-rw-r--r--src/core/hle/service/btm/btm.cpp8
-rw-r--r--src/core/hle/service/fatal/fatal_p.cpp8
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp81
-rw-r--r--src/core/hle/service/hid/errors.h7
-rw-r--r--src/core/hle/service/hid/hid.cpp10
-rw-r--r--src/core/hle/service/hid/irs.cpp324
-rw-r--r--src/core/hle/service/hid/irs.h295
-rw-r--r--src/core/hle/service/hid/irs_ring_lifo.h47
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.cpp265
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.h110
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.cpp150
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.h73
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.cpp27
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.h47
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.cpp34
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.h61
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.cpp26
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.h61
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.cpp67
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.h33
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp29
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.h53
-rw-r--r--src/core/hle/service/ldn/errors.h12
-rw-r--r--src/core/hle/service/ldn/ldn.cpp436
-rw-r--r--src/core/hle/service/ldn/ldn.h6
-rw-r--r--src/core/hle/service/ldn/ldn_results.h27
-rw-r--r--src/core/hle/service/ldn/ldn_types.h284
-rw-r--r--src/core/hle/service/nifm/nifm.cpp354
-rw-r--r--src/core/hle/service/nifm/nifm.h27
-rw-r--r--src/core/hle/service/nvflinger/binder.h1
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp18
-rw-r--r--src/core/hle/service/pcv/pcv.cpp93
-rw-r--r--src/core/hle/service/pcv/pcv.h91
-rw-r--r--src/core/hle/service/ptm/psm.cpp122
-rw-r--r--src/core/hle/service/ptm/psm.h31
-rw-r--r--src/core/hle/service/ptm/ptm.cpp18
-rw-r--r--src/core/hle/service/ptm/ptm.h18
-rw-r--r--src/core/hle/service/ptm/ts.cpp41
-rw-r--r--src/core/hle/service/ptm/ts.h25
-rw-r--r--src/core/hle/service/service.cpp11
-rw-r--r--src/core/hle/service/sockets/bsd.cpp64
-rw-r--r--src/core/hle/service/sockets/bsd.h15
-rw-r--r--src/core/hle/service/sockets/sockets.h6
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp4
-rw-r--r--src/core/hle/service/sockets/sockets_translate.h2
-rw-r--r--src/core/internal_network/network.cpp (renamed from src/core/network/network.cpp)68
-rw-r--r--src/core/internal_network/network.h (renamed from src/core/network/network.h)43
-rw-r--r--src/core/internal_network/network_interface.cpp (renamed from src/core/network/network_interface.cpp)2
-rw-r--r--src/core/internal_network/network_interface.h (renamed from src/core/network/network_interface.h)0
-rw-r--r--src/core/internal_network/socket_proxy.cpp284
-rw-r--r--src/core/internal_network/socket_proxy.h97
-rw-r--r--src/core/internal_network/sockets.h163
-rw-r--r--src/core/loader/elf.cpp263
-rw-r--r--src/core/loader/elf.h36
-rw-r--r--src/core/loader/kip.cpp2
-rw-r--r--src/core/loader/loader.cpp11
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp2
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/memory.cpp92
-rw-r--r--src/core/memory.h24
-rw-r--r--src/core/network/sockets.h94
-rw-r--r--src/core/perf_stats.cpp5
-rw-r--r--src/core/perf_stats.h5
-rw-r--r--src/core/telemetry_session.cpp5
-rw-r--r--src/core/telemetry_session.h5
139 files changed, 6184 insertions, 2958 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index d9357138f..8db9a3c65 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,8 +1,11 @@
+# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
+# 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
@@ -158,6 +161,7 @@ add_library(core STATIC
hid/input_converter.h
hid/input_interpreter.cpp
hid/input_interpreter.h
+ hid/irs_types.h
hid/motion_input.cpp
hid/motion_input.h
hle/api_version.h
@@ -445,6 +449,7 @@ add_library(core STATIC
hle/service/hid/hidbus.h
hle/service/hid/irs.cpp
hle/service/hid/irs.h
+ hle/service/hid/irs_ring_lifo.h
hle/service/hid/ring_lifo.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
@@ -477,15 +482,30 @@ add_library(core STATIC
hle/service/hid/hidbus/starlink.h
hle/service/hid/hidbus/stubbed.cpp
hle/service/hid/hidbus/stubbed.h
+ hle/service/hid/irsensor/clustering_processor.cpp
+ hle/service/hid/irsensor/clustering_processor.h
+ hle/service/hid/irsensor/image_transfer_processor.cpp
+ hle/service/hid/irsensor/image_transfer_processor.h
+ hle/service/hid/irsensor/ir_led_processor.cpp
+ hle/service/hid/irsensor/ir_led_processor.h
+ hle/service/hid/irsensor/moment_processor.cpp
+ hle/service/hid/irsensor/moment_processor.h
+ hle/service/hid/irsensor/pointing_processor.cpp
+ hle/service/hid/irsensor/pointing_processor.h
+ hle/service/hid/irsensor/processor_base.cpp
+ hle/service/hid/irsensor/processor_base.h
+ hle/service/hid/irsensor/tera_plugin_processor.cpp
+ hle/service/hid/irsensor/tera_plugin_processor.h
hle/service/jit/jit_context.cpp
hle/service/jit/jit_context.h
hle/service/jit/jit.cpp
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
@@ -605,6 +625,10 @@ add_library(core STATIC
hle/service/psc/psc.h
hle/service/ptm/psm.cpp
hle/service/ptm/psm.h
+ hle/service/ptm/ptm.cpp
+ hle/service/ptm/ptm.h
+ hle/service/ptm/ts.cpp
+ hle/service/ptm/ts.h
hle/service/kernel_helpers.cpp
hle/service/kernel_helpers.h
hle/service/service.cpp
@@ -695,10 +719,15 @@ add_library(core STATIC
hle/service/vi/vi_u.h
hle/service/wlan/wlan.cpp
hle/service/wlan/wlan.h
+ internal_network/network.cpp
+ internal_network/network.h
+ 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/elf.cpp
- loader/elf.h
loader/kip.cpp
loader/kip.h
loader/loader.cpp
@@ -722,11 +751,6 @@ add_library(core STATIC
memory/dmnt_cheat_vm.h
memory.cpp
memory.h
- network/network.cpp
- network/network.h
- network/network_interface.cpp
- network/network_interface.h
- network/sockets.h
perf_stats.cpp
perf_stats.h
reporter.cpp
@@ -761,8 +785,8 @@ endif()
create_target_directory_groups(core)
-target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
-target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
+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)
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
new file mode 100644
index 000000000..6737ce85a
--- /dev/null
+++ b/src/core/announce_multiplayer_session.cpp
@@ -0,0 +1,164 @@
+// 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() {
+ auto 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 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;
+ auto 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
new file mode 100644
index 000000000..db790f7d2
--- /dev/null
+++ b/src/core/announce_multiplayer_session.h
@@ -0,0 +1,98 @@
+// 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.cpp b/src/core/arm/arm_interface.cpp
index cef79b245..953d96439 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -147,7 +147,6 @@ void ARM_Interface::Run() {
// Notify the debugger and go to sleep if a watchpoint was hit.
if (Has(hr, watchpoint)) {
- RewindBreakpointInstruction();
if (system.DebuggerEnabled()) {
system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
}
@@ -155,9 +154,10 @@ void ARM_Interface::Run() {
break;
}
- // Handle syscalls and scheduling (this may change the current thread)
+ // Handle syscalls and scheduling (this may change the current thread/core)
if (Has(hr, svc_call)) {
Kernel::Svc::Call(system, GetSvcNumber());
+ break;
}
if (Has(hr, break_loop) || !uses_wall_clock) {
break;
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 8a066ed91..7d62d030e 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -28,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
@@ -37,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 {
@@ -182,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;
@@ -203,13 +202,12 @@ public:
static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
- static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5;
+ static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::MemoryAbort;
static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6;
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 1be5fe1c1..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"
@@ -52,7 +51,7 @@ public:
if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
return std::nullopt;
}
- return MemoryRead32(vaddr);
+ return memory.Read32(vaddr);
}
void MemoryWrite8(u32 vaddr, u8 value) override {
@@ -97,7 +96,7 @@ public:
parent.LogBacktrace();
LOG_ERROR(Core_ARM,
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
- num_instructions, MemoryRead32(pc));
+ num_instructions, memory.Read32(pc));
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
@@ -115,7 +114,7 @@ public:
parent.LogBacktrace();
LOG_CRITICAL(Core_ARM,
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
- exception, pc, MemoryRead32(pc), parent.IsInThumbMode());
+ exception, pc, memory.Read32(pc), parent.IsInThumbMode());
}
}
@@ -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);
}
@@ -155,7 +161,7 @@ public:
const auto match{parent.MatchingWatchpoint(addr, size, type)};
if (match) {
parent.halted_watchpoint = match;
- ReturnException(parent.jit.load()->Regs()[15], ARM_Interface::watchpoint);
+ parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
return false;
}
@@ -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,11 +212,10 @@ 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;
- config.far_code_offset = 400_MiB;
// Allow memory fault handling to work
if (system.DebuggerEnabled()) {
@@ -215,7 +226,6 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
if (!page_table) {
// Don't waste too much memory on null_jit
config.code_cache_size = 8_MiB;
- config.far_code_offset = 4_MiB;
}
// Safe optimizations
@@ -246,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;
@@ -313,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()} {}
@@ -396,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 c437f24b8..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"
@@ -56,7 +55,7 @@ public:
if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
return std::nullopt;
}
- return MemoryRead32(vaddr);
+ return memory.Read32(vaddr);
}
void MemoryWrite8(u64 vaddr, u8 value) override {
@@ -111,7 +110,7 @@ public:
parent.LogBacktrace();
LOG_ERROR(Core_ARM,
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
- num_instructions, MemoryRead32(pc));
+ num_instructions, memory.Read32(pc));
}
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
@@ -156,7 +155,7 @@ public:
parent.LogBacktrace();
LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
- static_cast<std::size_t>(exception), pc, MemoryRead32(pc));
+ static_cast<std::size_t>(exception), pc, memory.Read32(pc));
}
}
@@ -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);
}
@@ -198,7 +204,7 @@ public:
const auto match{parent.MatchingWatchpoint(addr, size, type)};
if (match) {
parent.halted_watchpoint = match;
- ReturnException(parent.jit.load()->GetPC(), ARM_Interface::watchpoint);
+ parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
return false;
}
@@ -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,11 +270,10 @@ 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;
- config.far_code_offset = 400_MiB;
// Allow memory fault handling to work
if (system.DebuggerEnabled()) {
@@ -275,7 +284,6 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
if (!page_table) {
// Don't waste too much memory on null_jit
config.code_cache_size = 8_MiB;
- config.far_code_offset = 4_MiB;
}
// Safe optimizations
@@ -306,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;
@@ -373,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()} {}
@@ -463,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 6aae79c48..200efe4db 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "common/logging/log.h"
@@ -9,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;
@@ -48,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 f271b2070..d90b3e568 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -36,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 7723d9782..ea32a4a8d 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <atomic>
@@ -8,6 +7,7 @@
#include <memory>
#include <utility>
+#include "audio_core/audio_core.h"
#include "common/fs/fs.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
@@ -42,14 +42,15 @@
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h"
+#include "core/internal_network/network.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
-#include "core/network/network.h"
#include "core/perf_stats.h"
#include "core/reporter.h"
#include "core/telemetry_session.h"
#include "core/tools/freezer.h"
+#include "network/network.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -129,7 +130,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
- : kernel{system}, fs_controller{system}, memory{system}, hid_core{},
+ : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
SystemResultStatus Run() {
@@ -140,6 +141,8 @@ struct System::Impl {
core_timing.SyncPause(false);
is_paused = false;
+ audio_core->PauseSinks(false);
+
return status;
}
@@ -147,6 +150,8 @@ 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;
@@ -154,6 +159,11 @@ struct System::Impl {
return status;
}
+ bool IsPaused() const {
+ std::unique_lock lk(suspend_guard);
+ return is_paused;
+ }
+
std::unique_lock<std::mutex> StallProcesses() {
std::unique_lock<std::mutex> lk(suspend_guard);
kernel.Suspend(true);
@@ -214,6 +224,8 @@ struct System::Impl {
return SystemResultStatus::ErrorVideoCore;
}
+ audio_core = std::make_unique<AudioCore::AudioCore>(system);
+
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
services = std::make_unique<Service::Services>(service_manager, system);
interrupt_manager = std::make_unique<Hardware::InterruptManager>(system);
@@ -290,7 +302,7 @@ struct System::Impl {
if (Settings::values.gamecard_current_game) {
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
} else if (!Settings::values.gamecard_path.GetValue().empty()) {
- const auto gamecard_path = Settings::values.gamecard_path.GetValue();
+ const auto& gamecard_path = Settings::values.gamecard_path.GetValue();
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, gamecard_path));
}
}
@@ -303,11 +315,24 @@ struct System::Impl {
GetAndResetPerfStats();
perf_stats->BeginSystemFrame();
+ std::string name = "Unknown Game";
+ if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) {
+ LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result);
+ }
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ Network::GameInfo game_info;
+ game_info.name = name;
+ game_info.id = program_id;
+ room_member->SendGameInfo(game_info);
+ }
+
status = SystemResultStatus::Success;
return status;
}
void Shutdown() {
+ SetShuttingDown(true);
+
// Log last frame performance stats if game was loded
if (perf_stats) {
const auto perf_results = GetAndResetPerfStats();
@@ -333,23 +358,37 @@ struct System::Impl {
kernel.ShutdownCores();
cpu_manager.Shutdown();
debugger.reset();
+ kernel.CloseServices();
services.reset();
service_manager.reset();
cheat_engine.reset();
telemetry_session.reset();
- cpu_manager.Shutdown();
time_manager.Shutdown();
core_timing.Shutdown();
app_loader.reset();
+ audio_core.reset();
gpu_core.reset();
perf_stats.reset();
kernel.Shutdown();
memory.Reset();
applet_manager.ClearAll();
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ Network::GameInfo game_info{};
+ room_member->SendGameInfo(game_info);
+ }
+
LOG_DEBUG(Core, "Shutdown OK");
}
+ bool IsShuttingDown() const {
+ return is_shutting_down;
+ }
+
+ void SetShuttingDown(bool shutting_down) {
+ is_shutting_down = shutting_down;
+ }
+
Loader::ResultStatus GetGameName(std::string& out) const {
if (app_loader == nullptr)
return Loader::ResultStatus::ErrorNotInitialized;
@@ -392,8 +431,9 @@ struct System::Impl {
return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
}
- std::mutex suspend_guard;
+ mutable std::mutex suspend_guard;
bool is_paused{};
+ std::atomic<bool> is_shutting_down{};
Timing::CoreTiming core_timing;
Kernel::KernelCore kernel;
@@ -407,8 +447,11 @@ struct System::Impl {
std::unique_ptr<Tegra::GPU> gpu_core;
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
std::unique_ptr<Core::DeviceMemory> device_memory;
+ std::unique_ptr<AudioCore::AudioCore> audio_core;
Core::Memory::Memory memory;
Core::HID::HIDCore hid_core;
+ Network::RoomNetwork room_network;
+
CpuManager cpu_manager;
std::atomic_bool is_powered_on{};
bool exit_lock = false;
@@ -479,6 +522,10 @@ SystemResultStatus System::Pause() {
return impl->Pause();
}
+bool System::IsPaused() const {
+ return impl->IsPaused();
+}
+
void System::InvalidateCpuInstructionCaches() {
impl->kernel.InvalidateAllInstructionCaches();
}
@@ -491,6 +538,14 @@ void System::Shutdown() {
impl->Shutdown();
}
+bool System::IsShuttingDown() const {
+ return impl->IsShuttingDown();
+}
+
+void System::SetShuttingDown(bool shutting_down) {
+ impl->SetShuttingDown(shutting_down);
+}
+
void System::DetachDebugger() {
if (impl->debugger) {
impl->debugger->NotifyShutdown();
@@ -640,6 +695,14 @@ const HID::HIDCore& System::HIDCore() const {
return impl->hid_core;
}
+AudioCore::AudioCore& System::AudioCore() {
+ return *impl->audio_core;
+}
+
+const AudioCore::AudioCore& System::AudioCore() const {
+ return *impl->audio_core;
+}
+
Timing::CoreTiming& System::CoreTiming() {
return impl->core_timing;
}
@@ -834,6 +897,14 @@ const Core::Debugger& System::GetDebugger() const {
return *impl->debugger;
}
+Network::RoomNetwork& System::GetRoomNetwork() {
+ return impl->room_network;
+}
+
+const Network::RoomNetwork& System::GetRoomNetwork() const {
+ return impl->room_network;
+}
+
void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
impl->execute_program_callback = std::move(callback);
}
diff --git a/src/core/core.h b/src/core/core.h
index 60efe4410..0ce3b1d60 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -81,6 +80,10 @@ namespace VideoCore {
class RendererBase;
} // namespace VideoCore
+namespace AudioCore {
+class AudioCore;
+} // namespace AudioCore
+
namespace Core::Timing {
class CoreTiming;
}
@@ -93,6 +96,10 @@ namespace Core::HID {
class HIDCore;
}
+namespace Network {
+class RoomNetwork;
+}
+
namespace Core {
class ARM_Interface;
@@ -148,6 +155,9 @@ public:
*/
[[nodiscard]] SystemResultStatus Pause();
+ /// Check if the core is currently paused.
+ [[nodiscard]] bool IsPaused() const;
+
/**
* Invalidate the CPU instruction caches
* This function should only be used by GDB Stub to support breakpoints, memory updates and
@@ -160,6 +170,12 @@ public:
/// Shutdown the emulated system.
void Shutdown();
+ /// Check if the core is shutting down.
+ [[nodiscard]] bool IsShuttingDown() const;
+
+ /// Set the shutting down state.
+ void SetShuttingDown(bool shutting_down);
+
/// Forcibly detach the debugger if it is running.
void DetachDebugger();
@@ -250,6 +266,12 @@ public:
/// Gets an immutable reference to the renderer.
[[nodiscard]] const VideoCore::RendererBase& Renderer() const;
+ /// Gets a mutable reference to the audio interface
+ [[nodiscard]] AudioCore::AudioCore& AudioCore();
+
+ /// Gets an immutable reference to the audio interface.
+ [[nodiscard]] const AudioCore::AudioCore& AudioCore() const;
+
/// Gets the global scheduler
[[nodiscard]] Kernel::GlobalSchedulerContext& GlobalSchedulerContext();
@@ -360,6 +382,12 @@ public:
[[nodiscard]] Core::Debugger& GetDebugger();
[[nodiscard]] const Core::Debugger& GetDebugger() const;
+ /// Gets a mutable reference to the Room Network.
+ [[nodiscard]] Network::RoomNetwork& GetRoomNetwork();
+
+ /// Gets an immutable reference to the Room Network.
+ [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const;
+
void SetExitLock(bool locked);
[[nodiscard]] bool GetExitLock() const;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 5425637f5..2dbb99c8b 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -6,9 +6,7 @@
#include <string>
#include <tuple>
-#include "common/logging/log.h"
#include "common/microprofile.h"
-#include "common/thread.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hardware_properties.h"
@@ -44,10 +42,10 @@ CoreTiming::CoreTiming()
CoreTiming::~CoreTiming() = default;
-void CoreTiming::ThreadEntry(CoreTiming& instance, size_t id) {
- const std::string name = "yuzu:HostTiming_" + std::to_string(id);
- MicroProfileOnThreadCreate(name.c_str());
- Common::SetCurrentThreadName(name.c_str());
+void CoreTiming::ThreadEntry(CoreTiming& instance) {
+ constexpr char name[] = "yuzu:HostTiming";
+ MicroProfileOnThreadCreate(name);
+ Common::SetCurrentThreadName(name);
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
instance.on_thread_init();
instance.ThreadLoop();
@@ -63,127 +61,100 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
-> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
if (is_multicore) {
- worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0);
+ timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
}
}
void CoreTiming::Shutdown() {
- is_paused = true;
+ paused = true;
shutting_down = true;
- std::atomic_thread_fence(std::memory_order_release);
-
- event_cv.notify_all();
- wait_pause_cv.notify_all();
- for (auto& thread : worker_threads) {
- thread.join();
+ pause_event.Set();
+ event.Set();
+ if (timer_thread) {
+ timer_thread->join();
}
- worker_threads.clear();
pause_callbacks.clear();
ClearPendingEvents();
+ timer_thread.reset();
has_started = false;
}
-void CoreTiming::Pause(bool is_paused_) {
- std::unique_lock main_lock(event_mutex);
- if (is_paused_ == paused_state.load(std::memory_order_relaxed)) {
- return;
- }
- if (is_multicore) {
- is_paused = is_paused_;
- event_cv.notify_all();
- if (!is_paused_) {
- wait_pause_cv.notify_all();
- }
- }
- paused_state.store(is_paused_, std::memory_order_relaxed);
+void CoreTiming::Pause(bool is_paused) {
+ paused = is_paused;
+ pause_event.Set();
- if (!is_paused_) {
+ if (!is_paused) {
pause_end_time = GetGlobalTimeNs().count();
}
for (auto& cb : pause_callbacks) {
- cb(is_paused_);
+ cb(is_paused);
}
}
-void CoreTiming::SyncPause(bool is_paused_) {
- std::unique_lock main_lock(event_mutex);
- if (is_paused_ == paused_state.load(std::memory_order_relaxed)) {
+void CoreTiming::SyncPause(bool is_paused) {
+ if (is_paused == paused && paused_set == paused) {
return;
}
- if (is_multicore) {
- is_paused = is_paused_;
- event_cv.notify_all();
- if (!is_paused_) {
- wait_pause_cv.notify_all();
- }
- }
- paused_state.store(is_paused_, std::memory_order_relaxed);
- if (is_multicore) {
- if (is_paused_) {
- wait_signal_cv.wait(main_lock, [this] { return pause_count == worker_threads.size(); });
- } else {
- wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; });
+ Pause(is_paused);
+ if (timer_thread) {
+ if (!is_paused) {
+ pause_event.Set();
}
+ event.Set();
+ while (paused_set != is_paused)
+ ;
}
- if (!is_paused_) {
+ if (!is_paused) {
pause_end_time = GetGlobalTimeNs().count();
}
for (auto& cb : pause_callbacks) {
- cb(is_paused_);
+ cb(is_paused);
}
}
bool CoreTiming::IsRunning() const {
- return !paused_state.load(std::memory_order_acquire);
+ return !paused_set;
}
bool CoreTiming::HasPendingEvents() const {
- std::unique_lock main_lock(event_mutex);
- return !event_queue.empty() || pending_events.load(std::memory_order_relaxed) != 0;
+ return !(wait_set && event_queue.empty());
}
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data, bool absolute_time) {
+ {
+ std::scoped_lock scope{basic_lock};
+ const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
- std::unique_lock main_lock(event_mutex);
- const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
-
- event_queue.emplace_back(Event{next_time.count(), event_fifo_id++, user_data, event_type, 0});
- pending_events.fetch_add(1, std::memory_order_relaxed);
-
- std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
-
- if (is_multicore) {
- event_cv.notify_one();
+ event_queue.emplace_back(
+ Event{next_time.count(), event_fifo_id++, user_data, event_type, 0});
+ std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
+
+ event.Set();
}
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
std::chrono::nanoseconds resched_time,
const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data, bool absolute_time) {
- std::unique_lock main_lock(event_mutex);
+ std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
event_queue.emplace_back(
Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
- pending_events.fetch_add(1, std::memory_order_relaxed);
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
-
- if (is_multicore) {
- event_cv.notify_one();
- }
}
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data) {
- std::unique_lock main_lock(event_mutex);
+ std::scoped_lock scope{basic_lock};
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type.lock().get() == event_type.get() && e.user_data == user_data;
});
@@ -192,7 +163,6 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
- pending_events.fetch_sub(1, std::memory_order_relaxed);
}
}
@@ -232,12 +202,11 @@ u64 CoreTiming::GetClockTicks() const {
}
void CoreTiming::ClearPendingEvents() {
- std::unique_lock main_lock(event_mutex);
event_queue.clear();
}
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
- std::unique_lock main_lock(event_mutex);
+ std::scoped_lock lock{basic_lock};
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type.lock().get() == event_type.get();
@@ -251,28 +220,27 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
}
void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) {
- std::unique_lock main_lock(event_mutex);
+ 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();
- std::unique_lock main_lock(event_mutex);
while (!event_queue.empty() && event_queue.front().time <= global_timer) {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
if (const auto event_type{evt.type.lock()}) {
- event_mutex.unlock();
+ basic_lock.unlock();
const auto new_schedule_time{event_type->callback(
evt.user_data, evt.time,
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
- event_mutex.lock();
- pending_events.fetch_sub(1, std::memory_order_relaxed);
+ basic_lock.lock();
if (evt.reschedule_time != 0) {
// If this event was scheduled into a pause, its time now is going to be way behind.
@@ -285,9 +253,9 @@ std::optional<s64> CoreTiming::Advance() {
const auto next_schedule_time{new_schedule_time.has_value()
? new_schedule_time.value().count()
: evt.reschedule_time};
+
event_queue.emplace_back(
Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time});
- pending_events.fetch_add(1, std::memory_order_relaxed);
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
@@ -304,34 +272,27 @@ std::optional<s64> CoreTiming::Advance() {
}
void CoreTiming::ThreadLoop() {
- const auto predicate = [this] { return !event_queue.empty() || is_paused; };
has_started = true;
while (!shutting_down) {
- while (!is_paused && !shutting_down) {
+ while (!paused) {
+ paused_set = false;
const auto next_time = Advance();
if (next_time) {
if (*next_time > 0) {
std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time);
- std::unique_lock main_lock(event_mutex);
- event_cv.wait_for(main_lock, next_time_ns, predicate);
+ event.WaitFor(next_time_ns);
}
} else {
- std::unique_lock main_lock(event_mutex);
- event_cv.wait(main_lock, predicate);
+ wait_set = true;
+ event.Wait();
}
+ wait_set = false;
}
- std::unique_lock main_lock(event_mutex);
- pause_count++;
- if (pause_count == worker_threads.size()) {
- clock->Pause(true);
- wait_signal_cv.notify_all();
- }
- wait_pause_cv.wait(main_lock, [this] { return !is_paused || shutting_down; });
- pause_count--;
- if (pause_count == 0) {
- clock->Pause(false);
- wait_signal_cv.notify_all();
- }
+
+ paused_set = true;
+ clock->Pause(true);
+ pause_event.Wait();
+ clock->Pause(false);
}
}
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 09b6ed81a..6aa3ae923 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -5,7 +5,6 @@
#include <atomic>
#include <chrono>
-#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
@@ -15,6 +14,7 @@
#include <vector>
#include "common/common_types.h"
+#include "common/thread.h"
#include "common/wall_clock.h"
namespace Core::Timing {
@@ -143,7 +143,7 @@ private:
/// Clear all pending events. This should ONLY be done on exit.
void ClearPendingEvents();
- static void ThreadEntry(CoreTiming& instance, size_t id);
+ static void ThreadEntry(CoreTiming& instance);
void ThreadLoop();
std::unique_ptr<Common::WallClock> clock;
@@ -156,24 +156,21 @@ private:
// accomodated by the standard adaptor class.
std::vector<Event> event_queue;
u64 event_fifo_id = 0;
- std::atomic<size_t> pending_events{};
std::shared_ptr<EventType> ev_lost;
+ Common::Event event{};
+ Common::Event pause_event{};
+ std::mutex basic_lock;
+ std::mutex advance_lock;
+ std::unique_ptr<std::thread> timer_thread;
+ std::atomic<bool> paused{};
+ std::atomic<bool> paused_set{};
+ std::atomic<bool> wait_set{};
+ std::atomic<bool> shutting_down{};
std::atomic<bool> has_started{};
std::function<void()> on_thread_init{};
- std::vector<std::thread> worker_threads;
-
- std::condition_variable event_cv;
- std::condition_variable wait_pause_cv;
- std::condition_variable wait_signal_cv;
- mutable std::mutex event_mutex;
-
- std::atomic<bool> paused_state{};
- bool is_paused{};
- bool shutting_down{};
bool is_multicore{};
- size_t pause_count{};
s64 pause_end_time{};
/// Cycle timing
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 37d3d83b9..9b1565ae1 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -8,6 +8,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/cpu_manager.h"
+#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
@@ -49,14 +50,6 @@ void CpuManager::GuestThreadFunction() {
}
}
-void CpuManager::GuestRewindFunction() {
- if (is_multicore) {
- MultiCoreRunGuestLoop();
- } else {
- SingleCoreRunGuestLoop();
- }
-}
-
void CpuManager::IdleThreadFunction() {
if (is_multicore) {
MultiCoreRunIdleThread();
@@ -69,21 +62,21 @@ void CpuManager::ShutdownThreadFunction() {
ShutdownThread();
}
+void CpuManager::HandleInterrupt() {
+ auto& kernel = system.Kernel();
+ auto core_index = kernel.CurrentPhysicalCoreIndex();
+
+ Kernel::KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_index));
+}
+
///////////////////////////////////////////////////////////////////////////////
/// MultiCore ///
///////////////////////////////////////////////////////////////////////////////
void CpuManager::MultiCoreRunGuestThread() {
+ // Similar to UserModeThreadStarter in HOS
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
- auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
- auto& host_context = thread->GetHostContext();
- host_context->SetRewindPoint([this] { GuestRewindFunction(); });
- MultiCoreRunGuestLoop();
-}
-
-void CpuManager::MultiCoreRunGuestLoop() {
- auto& kernel = system.Kernel();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
@@ -91,18 +84,26 @@ void CpuManager::MultiCoreRunGuestLoop() {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
- {
- Kernel::KScopedDisableDispatch dd(kernel);
- physical_core->ArmInterface().ClearExclusiveState();
- }
+
+ HandleInterrupt();
}
}
void CpuManager::MultiCoreRunIdleThread() {
+ // Not accurate to HOS. Remove this entire method when singlecore is removed.
+ // See notes in KScheduler::ScheduleImpl for more information about why this
+ // is inaccurate.
+
auto& kernel = system.Kernel();
+ kernel.CurrentScheduler()->OnThreadStart();
+
while (true) {
- Kernel::KScopedDisableDispatch dd(kernel);
- kernel.CurrentPhysicalCore().Idle();
+ auto& physical_core = kernel.CurrentPhysicalCore();
+ if (!physical_core.IsInterrupted()) {
+ physical_core.Idle();
+ }
+
+ HandleInterrupt();
}
}
@@ -113,80 +114,73 @@ void CpuManager::MultiCoreRunIdleThread() {
void CpuManager::SingleCoreRunGuestThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
- auto* thread = kernel.CurrentScheduler()->GetSchedulerCurrentThread();
- auto& host_context = thread->GetHostContext();
- host_context->SetRewindPoint([this] { GuestRewindFunction(); });
- SingleCoreRunGuestLoop();
-}
-void CpuManager::SingleCoreRunGuestLoop() {
- auto& kernel = system.Kernel();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
if (!physical_core->IsInterrupted()) {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
+
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
kernel.SetIsPhantomModeForSingleCore(false);
- physical_core->ArmInterface().ClearExclusiveState();
+
PreemptSingleCore();
- auto& scheduler = kernel.Scheduler(current_core);
- scheduler.RescheduleCurrentCore();
+ HandleInterrupt();
}
}
void CpuManager::SingleCoreRunIdleThread() {
auto& kernel = system.Kernel();
+ kernel.CurrentScheduler()->OnThreadStart();
+
while (true) {
- auto& physical_core = kernel.CurrentPhysicalCore();
PreemptSingleCore(false);
system.CoreTiming().AddTicks(1000U);
idle_count++;
- auto& scheduler = physical_core.Scheduler();
- scheduler.RescheduleCurrentCore();
+ HandleInterrupt();
}
}
-void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
- {
- auto& kernel = system.Kernel();
- auto& scheduler = kernel.Scheduler(current_core);
- Kernel::KThread* current_thread = scheduler.GetSchedulerCurrentThread();
- if (idle_count >= 4 || from_running_enviroment) {
- if (!from_running_enviroment) {
- system.CoreTiming().Idle();
- idle_count = 0;
- }
- kernel.SetIsPhantomModeForSingleCore(true);
- system.CoreTiming().Advance();
- kernel.SetIsPhantomModeForSingleCore(false);
- }
- current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
- system.CoreTiming().ResetTicks();
- scheduler.Unload(scheduler.GetSchedulerCurrentThread());
-
- auto& next_scheduler = kernel.Scheduler(current_core);
- Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
- }
+void CpuManager::PreemptSingleCore(bool from_running_environment) {
+ auto& kernel = system.Kernel();
- // May have changed scheduler
- {
- auto& scheduler = system.Kernel().Scheduler(current_core);
- scheduler.Reload(scheduler.GetSchedulerCurrentThread());
- if (!scheduler.IsIdle()) {
+ if (idle_count >= 4 || from_running_environment) {
+ if (!from_running_environment) {
+ system.CoreTiming().Idle();
idle_count = 0;
}
+ kernel.SetIsPhantomModeForSingleCore(true);
+ system.CoreTiming().Advance();
+ kernel.SetIsPhantomModeForSingleCore(false);
+ }
+ current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
+ system.CoreTiming().ResetTicks();
+ kernel.Scheduler(current_core).PreemptSingleCore();
+
+ // We've now been scheduled again, and we may have exchanged schedulers.
+ // Reload the scheduler in case it's different.
+ if (!kernel.Scheduler(current_core).IsIdle()) {
+ idle_count = 0;
}
}
+void CpuManager::GuestActivate() {
+ // Similar to the HorizonKernelMain callback in HOS
+ auto& kernel = system.Kernel();
+ auto* scheduler = kernel.CurrentScheduler();
+
+ scheduler->Activate();
+ UNREACHABLE();
+}
+
void CpuManager::ShutdownThread() {
auto& kernel = system.Kernel();
+ auto* thread = kernel.GetCurrentEmuThread();
auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0;
- auto* current_thread = kernel.GetCurrentEmuThread();
- Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
+ Common::Fiber::YieldTo(thread->GetHostContext(), *core_data[core].host_context);
UNREACHABLE();
}
@@ -218,9 +212,12 @@ void CpuManager::RunThread(std::size_t core) {
system.GPU().ObtainContext();
}
- auto* current_thread = system.Kernel().CurrentScheduler()->GetIdleThread();
- Kernel::SetCurrentThread(system.Kernel(), current_thread);
- Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
+ auto& kernel = system.Kernel();
+ auto& scheduler = *kernel.CurrentScheduler();
+ auto* thread = scheduler.GetSchedulerCurrentThread();
+ Kernel::SetCurrentThread(kernel, thread);
+
+ Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
}
} // namespace Core
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 76dc58ee1..95ea3ef39 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -50,7 +50,10 @@ public:
void Initialize();
void Shutdown();
- std::function<void()> GetGuestThreadStartFunc() {
+ std::function<void()> GetGuestActivateFunc() {
+ return [this] { GuestActivate(); };
+ }
+ std::function<void()> GetGuestThreadFunc() {
return [this] { GuestThreadFunction(); };
}
std::function<void()> GetIdleThreadStartFunc() {
@@ -68,20 +71,19 @@ public:
private:
void GuestThreadFunction();
- void GuestRewindFunction();
void IdleThreadFunction();
void ShutdownThreadFunction();
void MultiCoreRunGuestThread();
- void MultiCoreRunGuestLoop();
void MultiCoreRunIdleThread();
void SingleCoreRunGuestThread();
- void SingleCoreRunGuestLoop();
void SingleCoreRunIdleThread();
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
+ void GuestActivate();
+ void HandleInterrupt();
void ShutdownThread();
void RunThread(std::size_t core);
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/errors.h b/src/core/file_sys/errors.h
index ff15b3e23..7cee0c7df 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
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/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h
index a405e3c94..094d1e713 100644
--- a/src/core/frontend/applets/software_keyboard.h
+++ b/src/core/frontend/applets/software_keyboard.h
@@ -17,6 +17,8 @@ struct KeyboardInitializeParameters {
std::u16string sub_text;
std::u16string guide_text;
std::u16string initial_text;
+ char16_t left_optional_symbol_key;
+ char16_t right_optional_symbol_key;
u32 max_text_length;
u32 min_text_length;
s32 initial_cursor_position;
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 57c6ffc43..1be2dccb0 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include "core/frontend/emu_window.h"
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index b3bffecb2..ac1906d5e 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 62a7a1a19..f9f902c2d 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,18 +85,19 @@ 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));
@@ -128,10 +130,14 @@ void EmulatedController::LoadDevices() {
battery_params[LeftIndex].Set("battery", true);
battery_params[RightIndex].Set("battery", true);
+ camera_params = Common::ParamPackage{"engine:camera,camera:1"};
+
output_params[LeftIndex] = left_joycon;
output_params[RightIndex] = right_joycon;
+ output_params[2] = camera_params;
output_params[LeftIndex].Set("output", true);
output_params[RightIndex].Set("output", true);
+ output_params[2].Set("output", true);
LoadTASParams();
@@ -148,6 +154,7 @@ void EmulatedController::LoadDevices() {
Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
Common::Input::CreateDevice<Common::Input::InputDevice>);
+ camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
Common::Input::CreateDevice<Common::Input::OutputDevice>);
@@ -269,6 +276,14 @@ void EmulatedController::ReloadInput() {
motion_devices[index]->ForceUpdate();
}
+ if (camera_devices) {
+ camera_devices->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
+ });
+ camera_devices->ForceUpdate();
+ }
+
// Use a common UUID for TAS
static constexpr Common::UUID TAS_UUID = Common::UUID{
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
@@ -854,6 +869,25 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
TriggerOnChange(ControllerTriggerType::Battery, true);
}
+void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
+ std::unique_lock lock{mutex};
+ controller.camera_values = TransformToCamera(callback);
+
+ if (is_configuring) {
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::IrSensor, false);
+ return;
+ }
+
+ controller.camera_state.sample++;
+ controller.camera_state.format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
+ controller.camera_state.data = controller.camera_values.data;
+
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::IrSensor, true);
+}
+
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
if (device_index >= output_devices.size()) {
return false;
@@ -920,6 +954,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;
@@ -931,6 +968,23 @@ bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode)
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
}
+bool EmulatedController::SetCameraFormat(
+ Core::IrSensor::ImageTransferProcessorFormat camera_format) {
+ LOG_INFO(Service_HID, "Set camera format {}", camera_format);
+
+ auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
+ auto& camera_output_device = output_devices[2];
+
+ if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
+ camera_format)) == Common::Input::CameraError::None) {
+ return true;
+ }
+
+ // Fallback to Qt camera if native device doesn't have support
+ return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
+ camera_format)) == Common::Input::CameraError::None;
+}
+
void EmulatedController::SetLedPattern() {
for (auto& device : output_devices) {
if (!device) {
@@ -1196,6 +1250,11 @@ BatteryValues EmulatedController::GetBatteryValues() const {
return controller.battery_values;
}
+CameraValues EmulatedController::GetCameraValues() const {
+ std::scoped_lock lock{mutex};
+ return controller.camera_values;
+}
+
HomeButtonState EmulatedController::GetHomeButtons() const {
std::scoped_lock lock{mutex};
if (is_configuring) {
@@ -1284,6 +1343,20 @@ BatteryLevelState EmulatedController::GetBattery() const {
return controller.battery_state;
}
+const CameraState& EmulatedController::GetCamera() const {
+ std::scoped_lock lock{mutex};
+ 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 1469fb95a..c3aa8f9d3 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -15,10 +15,12 @@
#include "common/settings.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
+#include "core/hid/irs_types.h"
#include "core/hid/motion_input.h"
namespace Core::HID {
const std::size_t max_emulated_controllers = 2;
+const std::size_t output_devices = 3;
struct ControllerMotionInfo {
Common::Input::MotionStatus raw_status{};
MotionInput emulated{};
@@ -34,15 +36,16 @@ using TriggerDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
using BatteryDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
-using OutputDevices =
- std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
+using CameraDevices = std::unique_ptr<Common::Input::InputDevice>;
+using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>;
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
-using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
+using CameraParams = Common::ParamPackage;
+using OutputParams = std::array<Common::ParamPackage, output_devices>;
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
@@ -51,6 +54,7 @@ using TriggerValues =
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
+using CameraValues = Common::Input::CameraStatus;
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
struct AnalogSticks {
@@ -70,6 +74,12 @@ struct BatteryLevelState {
NpadPowerInfo right{};
};
+struct CameraState {
+ Core::IrSensor::ImageTransferProcessorFormat format{};
+ std::vector<u8> data{};
+ std::size_t sample{};
+};
+
struct ControllerMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
@@ -96,6 +106,7 @@ struct ControllerStatus {
ColorValues color_values{};
BatteryValues battery_values{};
VibrationValues vibration_values{};
+ CameraValues camera_values{};
// Data for HID serices
HomeButtonState home_button_state{};
@@ -107,6 +118,7 @@ struct ControllerStatus {
NpadGcTriggerState gc_trigger_state{};
ControllerColors colors_state{};
BatteryLevelState battery_state{};
+ CameraState camera_state{};
};
enum class ControllerTriggerType {
@@ -117,6 +129,7 @@ enum class ControllerTriggerType {
Color,
Battery,
Vibration,
+ IrSensor,
Connected,
Disconnected,
Type,
@@ -269,6 +282,9 @@ public:
/// Returns the latest battery status from the controller with parameters
BatteryValues GetBatteryValues() const;
+ /// Returns the latest camera status from the controller with parameters
+ CameraValues GetCameraValues() const;
+
/// Returns the latest status of button input for the hid::HomeButton service
HomeButtonState GetHomeButtons() const;
@@ -296,6 +312,9 @@ public:
/// Returns the latest battery status from the controller
BatteryLevelState GetBattery() const;
+ /// Returns the latest camera status from the controller
+ const CameraState& GetCamera() const;
+
/**
* Sends a specific vibration to the output device
* @return true if vibration had no errors
@@ -315,6 +334,13 @@ public:
*/
bool SetPollingMode(Common::Input::PollingMode polling_mode);
+ /**
+ * Sets the desired camera format to be polled from a controller
+ * @param camera_format size of each frame
+ * @return true if SetCameraFormat was successfull
+ */
+ bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
+
/// Returns the led pattern corresponding to this emulated controller
LedPattern GetLedPattern() const;
@@ -393,6 +419,19 @@ private:
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
+ * Updates the camera status of the controller
+ * @param callback A CallbackStatus containing the camera status
+ */
+ 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
@@ -418,6 +457,7 @@ private:
ControllerMotionParams motion_params;
TriggerParams trigger_params;
BatteryParams battery_params;
+ CameraParams camera_params;
OutputParams output_params;
ButtonDevices button_devices;
@@ -425,6 +465,7 @@ private:
ControllerMotionDevices motion_devices;
TriggerDevices trigger_devices;
BatteryDevices battery_devices;
+ CameraDevices camera_devices;
OutputDevices output_devices;
// TAS related variables
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 18d9f042d..68d143a01 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -270,6 +270,20 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu
return status;
}
+Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) {
+ Common::Input::CameraStatus camera{};
+ switch (callback.type) {
+ case Common::Input::InputType::IrSensor:
+ camera = callback.camera_status;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
+ break;
+ }
+
+ return camera;
+}
+
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
const auto& properties = analog.properties;
float& raw_value = analog.raw_value;
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
index 2be36889f..143c50cc0 100644
--- a/src/core/hid/input_converter.h
+++ b/src/core/hid/input_converter.h
@@ -77,6 +77,14 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
/**
+ * Converts raw input data into a valid camera status.
+ *
+ * @param callback Supported callbacks: Camera.
+ * @return A valid CameraObject object.
+ */
+Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);
+
+/**
* Converts raw analog data into a valid analog value
* @param analog An analog object containing raw data and properties
* @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h
new file mode 100644
index 000000000..88c5b016d
--- /dev/null
+++ b/src/core/hid/irs_types.h
@@ -0,0 +1,301 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hid/hid_types.h"
+
+namespace Core::IrSensor {
+
+// This is nn::irsensor::CameraAmbientNoiseLevel
+enum class CameraAmbientNoiseLevel : u32 {
+ Low,
+ Medium,
+ High,
+ Unkown3, // This level can't be reached
+};
+
+// This is nn::irsensor::CameraLightTarget
+enum class CameraLightTarget : u32 {
+ AllLeds,
+ BrightLeds,
+ DimLeds,
+ None,
+};
+
+// This is nn::irsensor::PackedCameraLightTarget
+enum class PackedCameraLightTarget : u8 {
+ AllLeds,
+ BrightLeds,
+ DimLeds,
+ None,
+};
+
+// This is nn::irsensor::AdaptiveClusteringMode
+enum class AdaptiveClusteringMode : u32 {
+ StaticFov,
+ DynamicFov,
+};
+
+// This is nn::irsensor::AdaptiveClusteringTargetDistance
+enum class AdaptiveClusteringTargetDistance : u32 {
+ Near,
+ Middle,
+ Far,
+};
+
+// This is nn::irsensor::ImageTransferProcessorFormat
+enum class ImageTransferProcessorFormat : u32 {
+ Size320x240,
+ Size160x120,
+ Size80x60,
+ Size40x30,
+ Size20x15,
+};
+
+// This is nn::irsensor::PackedImageTransferProcessorFormat
+enum class PackedImageTransferProcessorFormat : u8 {
+ Size320x240,
+ Size160x120,
+ Size80x60,
+ Size40x30,
+ Size20x15,
+};
+
+// This is nn::irsensor::IrCameraStatus
+enum class IrCameraStatus : u32 {
+ Available,
+ Unsupported,
+ Unconnected,
+};
+
+// This is nn::irsensor::IrCameraInternalStatus
+enum class IrCameraInternalStatus : u32 {
+ Stopped,
+ FirmwareUpdateNeeded,
+ Unkown2,
+ Unkown3,
+ Unkown4,
+ FirmwareVersionRequested,
+ FirmwareVersionIsInvalid,
+ Ready,
+ Setting,
+};
+
+// This is nn::irsensor::detail::StatusManager::IrSensorMode
+enum class IrSensorMode : u64 {
+ None,
+ MomentProcessor,
+ ClusteringProcessor,
+ ImageTransferProcessor,
+ PointingProcessorMarker,
+ TeraPluginProcessor,
+ IrLedProcessor,
+};
+
+// This is nn::irsensor::ImageProcessorStatus
+enum ImageProcessorStatus : u32 {
+ Stopped,
+ Running,
+};
+
+// This is nn::irsensor::HandAnalysisMode
+enum class HandAnalysisMode : u32 {
+ None,
+ Silhouette,
+ Image,
+ SilhoueteAndImage,
+ SilhuetteOnly,
+};
+
+// This is nn::irsensor::IrSensorFunctionLevel
+enum class IrSensorFunctionLevel : u8 {
+ unknown0,
+ unknown1,
+ unknown2,
+ unknown3,
+ unknown4,
+};
+
+// This is nn::irsensor::MomentProcessorPreprocess
+enum class MomentProcessorPreprocess : u32 {
+ Unkown0,
+ Unkown1,
+};
+
+// This is nn::irsensor::PackedMomentProcessorPreprocess
+enum class PackedMomentProcessorPreprocess : u8 {
+ Unkown0,
+ Unkown1,
+};
+
+// This is nn::irsensor::PointingStatus
+enum class PointingStatus : u32 {
+ Unkown0,
+ Unkown1,
+};
+
+struct IrsRect {
+ s16 x;
+ s16 y;
+ s16 width;
+ s16 height;
+};
+
+struct IrsCentroid {
+ f32 x;
+ f32 y;
+};
+
+struct CameraConfig {
+ u64 exposure_time;
+ CameraLightTarget light_target;
+ u32 gain;
+ bool is_negative_used;
+ INSERT_PADDING_BYTES(7);
+};
+static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size");
+
+struct PackedCameraConfig {
+ u64 exposure_time;
+ PackedCameraLightTarget light_target;
+ u8 gain;
+ bool is_negative_used;
+ INSERT_PADDING_BYTES(5);
+};
+static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size");
+
+// This is nn::irsensor::IrCameraHandle
+struct IrCameraHandle {
+ u8 npad_id{};
+ Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
+
+// This is nn::irsensor::PackedMcuVersion
+struct PackedMcuVersion {
+ u16 major;
+ u16 minor;
+};
+static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
+
+// This is nn::irsensor::PackedMomentProcessorConfig
+struct PackedMomentProcessorConfig {
+ PackedCameraConfig camera_config;
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+ PackedMomentProcessorPreprocess preprocess;
+ u8 preprocess_intensity_threshold;
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
+ "PackedMomentProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedClusteringProcessorConfig
+struct PackedClusteringProcessorConfig {
+ PackedCameraConfig camera_config;
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+ u32 pixel_count_min;
+ u32 pixel_count_max;
+ u8 object_intensity_min;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28,
+ "PackedClusteringProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedImageTransferProcessorConfig
+struct PackedImageTransferProcessorConfig {
+ PackedCameraConfig camera_config;
+ PackedMcuVersion required_mcu_version;
+ PackedImageTransferProcessorFormat format;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
+ "PackedImageTransferProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedTeraPluginProcessorConfig
+struct PackedTeraPluginProcessorConfig {
+ PackedMcuVersion required_mcu_version;
+ u8 mode;
+ u8 unknown_1;
+ u8 unknown_2;
+ u8 unknown_3;
+};
+static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
+ "PackedTeraPluginProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedPointingProcessorConfig
+struct PackedPointingProcessorConfig {
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+};
+static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
+ "PackedPointingProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedFunctionLevel
+struct PackedFunctionLevel {
+ IrSensorFunctionLevel function_level;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
+
+// This is nn::irsensor::PackedImageTransferProcessorExConfig
+struct PackedImageTransferProcessorExConfig {
+ PackedCameraConfig camera_config;
+ PackedMcuVersion required_mcu_version;
+ PackedImageTransferProcessorFormat origin_format;
+ PackedImageTransferProcessorFormat trimming_format;
+ u16 trimming_start_x;
+ u16 trimming_start_y;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(5);
+};
+static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
+ "PackedImageTransferProcessorExConfig is an invalid size");
+
+// This is nn::irsensor::PackedIrLedProcessorConfig
+struct PackedIrLedProcessorConfig {
+ PackedMcuVersion required_mcu_version;
+ u8 light_target;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
+ "PackedIrLedProcessorConfig is an invalid size");
+
+// This is nn::irsensor::HandAnalysisConfig
+struct HandAnalysisConfig {
+ HandAnalysisMode mode;
+};
+static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size");
+
+// This is nn::irsensor::detail::ProcessorState contents are different for each processor
+struct ProcessorState {
+ std::array<u8, 0xE20> processor_raw_data{};
+};
+static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size");
+
+// This is nn::irsensor::detail::DeviceFormat
+struct DeviceFormat {
+ Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected};
+ Core::IrSensor::IrCameraInternalStatus camera_internal_status{
+ Core::IrSensor::IrCameraInternalStatus::Ready};
+ Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None};
+ ProcessorState state{};
+};
+static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size");
+
+// This is nn::irsensor::ImageTransferProcessorState
+struct ImageTransferProcessorState {
+ u64 sampling_number;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ INSERT_PADDING_BYTES(4);
+};
+static_assert(sizeof(ImageTransferProcessorState) == 0x10,
+ "ImageTransferProcessorState is an invalid size");
+
+} // namespace Core::IrSensor
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 602e12606..416da15ec 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 004bb2005..d631c0357 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp
index 164436b26..65576b8c4 100644
--- a/src/core/hle/kernel/global_scheduler_context.cpp
+++ b/src/core/hle/kernel/global_scheduler_context.cpp
@@ -41,12 +41,7 @@ void GlobalSchedulerContext::PreemptThreads() {
ASSERT(IsLocked());
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
const u32 priority = preemption_priorities[core_id];
- kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
-
- // Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
- // in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
- // interrupts that may have occurred.
- kernel.PhysicalCore(core_id).Interrupt();
+ KScheduler::RotateScheduledQueue(kernel, core_id, priority);
}
}
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 45135a07f..5b3feec66 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -287,18 +287,52 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size() >= size,
{ return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
- memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
+ WriteBufferB(buffer, size, buffer_index);
} else {
ASSERT_OR_EXECUTE_MSG(
BufferDescriptorC().size() > buffer_index &&
BufferDescriptorC()[buffer_index].Size() >= size,
{ return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
- memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
+ WriteBufferC(buffer, size, buffer_index);
}
return size;
}
+std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size,
+ std::size_t buffer_index) const {
+ if (buffer_index >= BufferDescriptorB().size() || size == 0) {
+ return 0;
+ }
+
+ const auto buffer_size{BufferDescriptorB()[buffer_index].Size()};
+ if (size > buffer_size) {
+ LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
+ buffer_size);
+ size = buffer_size; // TODO(bunnei): This needs to be HW tested
+ }
+
+ memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
+ return size;
+}
+
+std::size_t HLERequestContext::WriteBufferC(const void* buffer, std::size_t size,
+ std::size_t buffer_index) const {
+ if (buffer_index >= BufferDescriptorC().size() || size == 0) {
+ return 0;
+ }
+
+ const auto buffer_size{BufferDescriptorC()[buffer_index].Size()};
+ if (size > buffer_size) {
+ LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
+ buffer_size);
+ size = buffer_size; // TODO(bunnei): This needs to be HW tested
+ }
+
+ memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
+ return size;
+}
+
std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const {
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index d3abeee85..99265ce90 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -277,6 +277,14 @@ public:
std::size_t WriteBuffer(const void* buffer, std::size_t size,
std::size_t buffer_index = 0) const;
+ /// Helper function to write buffer B
+ std::size_t WriteBufferB(const void* buffer, std::size_t size,
+ std::size_t buffer_index = 0) const;
+
+ /// Helper function to write buffer C
+ std::size_t WriteBufferC(const void* buffer, std::size_t size,
+ std::size_t buffer_index = 0) const;
+
/* Helper function to write a buffer using the appropriate buffer descriptor
*
* @tparam T an arbitrary container that satisfies the
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index d63e77d15..3cb22ff4d 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/hle/kernel/hle_ipc.h"
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
index ef8583efc..e17eff28f 100644
--- a/src/core/hle/kernel/k_client_port.h
+++ b/src/core/hle/kernel/k_client_port.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
index 2410f74a3..2e7e1436a 100644
--- a/src/core/hle/kernel/k_code_memory.h
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -30,19 +30,19 @@ public:
explicit KCodeMemory(KernelCore& kernel_);
Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
- void Finalize();
+ void Finalize() override;
Result Map(VAddr address, size_t size);
Result Unmap(VAddr address, size_t size);
Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
Result UnmapFromOwner(VAddr address, size_t size);
- bool IsInitialized() const {
+ bool IsInitialized() const override {
return m_is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
- KProcess* GetOwner() const {
+ KProcess* GetOwner() const override {
return m_owner;
}
VAddr GetSourceAddress() const {
diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp
index d606a7f86..1b577a5b3 100644
--- a/src/core/hle/kernel/k_interrupt_manager.cpp
+++ b/src/core/hle/kernel/k_interrupt_manager.cpp
@@ -6,6 +6,7 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
namespace Kernel::KInterruptManager {
@@ -15,6 +16,9 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
return;
}
+ // Acknowledge the interrupt.
+ kernel.PhysicalCore(core_id).ClearInterrupt();
+
auto& current_thread = GetCurrentThread(kernel);
// If the user disable count is set, we may need to pin the current thread.
@@ -27,6 +31,9 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
// Set the interrupt flag for the thread.
GetCurrentThread(kernel).SetInterruptFlag();
}
+
+ // Request interrupt scheduling.
+ kernel.CurrentScheduler()->RequestScheduleOnInterrupt();
}
} // namespace Kernel::KInterruptManager
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index b662788b3..d3e99665f 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -1,6 +1,5 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <bitset>
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 5e3e22ad8..d56d73bab 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -1,6 +1,5 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index d599d2bcb..c34ce7a17 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -27,69 +27,185 @@ static void IncrementScheduledCount(Kernel::KThread* thread) {
}
}
-void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) {
- auto scheduler = kernel.CurrentScheduler();
-
- u32 current_core{0xF};
- bool must_context_switch{};
- if (scheduler) {
- current_core = scheduler->core_id;
- // TODO(bunnei): Should be set to true when we deprecate single core
- must_context_switch = !kernel.IsPhantomModeForSingleCore();
- }
-
- while (cores_pending_reschedule != 0) {
- const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
- ASSERT(core < Core::Hardware::NUM_CPU_CORES);
- if (!must_context_switch || core != current_core) {
- auto& phys_core = kernel.PhysicalCore(core);
- phys_core.Interrupt();
+KScheduler::KScheduler(KernelCore& kernel_) : kernel{kernel_} {
+ m_switch_fiber = std::make_shared<Common::Fiber>([this] {
+ while (true) {
+ ScheduleImplFiber();
}
- cores_pending_reschedule &= ~(1ULL << core);
+ });
+
+ m_state.needs_scheduling = true;
+}
+
+KScheduler::~KScheduler() = default;
+
+void KScheduler::SetInterruptTaskRunnable() {
+ m_state.interrupt_task_runnable = true;
+ m_state.needs_scheduling = true;
+}
+
+void KScheduler::RequestScheduleOnInterrupt() {
+ m_state.needs_scheduling = true;
+
+ if (CanSchedule(kernel)) {
+ ScheduleOnInterrupt();
}
+}
- for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
- if (kernel.PhysicalCore(core_id).IsInterrupted()) {
- KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
- }
+void KScheduler::DisableScheduling(KernelCore& kernel) {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
+ GetCurrentThread(kernel).DisableDispatch();
+}
+
+void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 1);
+
+ auto* scheduler{kernel.CurrentScheduler()};
+
+ if (!scheduler || kernel.IsPhantomModeForSingleCore()) {
+ KScheduler::RescheduleCores(kernel, cores_needing_scheduling);
+ KScheduler::RescheduleCurrentHLEThread(kernel);
+ return;
+ }
+
+ scheduler->RescheduleOtherCores(cores_needing_scheduling);
+
+ if (GetCurrentThread(kernel).GetDisableDispatchCount() > 1) {
+ GetCurrentThread(kernel).EnableDispatch();
+ } else {
+ scheduler->RescheduleCurrentCore();
+ }
+}
+
+void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) {
+ // HACK: we cannot schedule from this thread, it is not a core thread
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ // Special case to ensure dummy threads that are waiting block
+ GetCurrentThread(kernel).IfDummyThreadTryWait();
+
+ ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting);
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
+ if (IsSchedulerUpdateNeeded(kernel)) {
+ return UpdateHighestPriorityThreadsImpl(kernel);
+ } else {
+ return 0;
+ }
+}
+
+void KScheduler::Schedule() {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+ ASSERT(m_core_id == GetCurrentCoreId(kernel));
+
+ ScheduleImpl();
+}
+
+void KScheduler::ScheduleOnInterrupt() {
+ GetCurrentThread(kernel).DisableDispatch();
+ Schedule();
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+void KScheduler::PreemptSingleCore() {
+ GetCurrentThread(kernel).DisableDispatch();
+
+ auto* thread = GetCurrentThreadPointer(kernel);
+ auto& previous_scheduler = kernel.Scheduler(thread->GetCurrentCore());
+ previous_scheduler.Unload(thread);
+
+ Common::Fiber::YieldTo(thread->GetHostContext(), *m_switch_fiber);
+
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+void KScheduler::RescheduleCurrentCore() {
+ ASSERT(!kernel.IsPhantomModeForSingleCore());
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ GetCurrentThread(kernel).EnableDispatch();
+
+ if (m_state.needs_scheduling.load()) {
+ // Disable interrupts, and then check again if rescheduling is needed.
+ // KScopedInterruptDisable intr_disable;
+
+ kernel.CurrentScheduler()->RescheduleCurrentCoreImpl();
}
+}
- if (must_context_switch) {
- auto core_scheduler = kernel.CurrentScheduler();
- kernel.ExitSVCProfile();
- core_scheduler->RescheduleCurrentCore();
- kernel.EnterSVCProfile();
+void KScheduler::RescheduleCurrentCoreImpl() {
+ // Check that scheduling is needed.
+ if (m_state.needs_scheduling.load()) [[likely]] {
+ GetCurrentThread(kernel).DisableDispatch();
+ Schedule();
+ GetCurrentThread(kernel).EnableDispatch();
}
}
+void KScheduler::Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id) {
+ // Set core ID/idle thread/interrupt task manager.
+ m_core_id = core_id;
+ m_idle_thread = idle_thread;
+ // m_state.idle_thread_stack = m_idle_thread->GetStackTop();
+ // m_state.interrupt_task_manager = &kernel.GetInterruptTaskManager();
+
+ // Insert the main thread into the priority queue.
+ // {
+ // KScopedSchedulerLock lk{kernel};
+ // GetPriorityQueue(kernel).PushBack(GetCurrentThreadPointer(kernel));
+ // SetSchedulerUpdateNeeded(kernel);
+ // }
+
+ // Bind interrupt handler.
+ // kernel.GetInterruptManager().BindHandler(
+ // GetSchedulerInterruptHandler(kernel), KInterruptName::Scheduler, m_core_id,
+ // KInterruptController::PriorityLevel::Scheduler, false, false);
+
+ // Set the current thread.
+ m_current_thread = main_thread;
+}
+
+void KScheduler::Activate() {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ // m_state.should_count_idle = KTargetSystem::IsDebugMode();
+ m_is_active = true;
+ RescheduleCurrentCore();
+}
+
+void KScheduler::OnThreadStart() {
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
- KScopedSpinLock lk{guard};
- if (KThread* prev_highest_thread = state.highest_priority_thread;
- prev_highest_thread != highest_thread) {
- if (prev_highest_thread != nullptr) {
+ if (KThread* prev_highest_thread = m_state.highest_priority_thread;
+ prev_highest_thread != highest_thread) [[likely]] {
+ if (prev_highest_thread != nullptr) [[likely]] {
IncrementScheduledCount(prev_highest_thread);
- prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks());
+ prev_highest_thread->SetLastScheduledTick(kernel.System().CoreTiming().GetCPUTicks());
}
- if (state.should_count_idle) {
- if (highest_thread != nullptr) {
+ if (m_state.should_count_idle) {
+ if (highest_thread != nullptr) [[likely]] {
if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) {
- process->SetRunningThread(core_id, highest_thread, state.idle_count);
+ process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count);
}
} else {
- state.idle_count++;
+ m_state.idle_count++;
}
}
- state.highest_priority_thread = highest_thread;
- state.needs_scheduling.store(true);
- return (1ULL << core_id);
+ m_state.highest_priority_thread = highest_thread;
+ m_state.needs_scheduling = true;
+ return (1ULL << m_core_id);
} else {
return 0;
}
}
u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Clear that we need to update.
ClearSchedulerUpdateNeeded(kernel);
@@ -98,18 +214,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
KThread* top_threads[Core::Hardware::NUM_CPU_CORES];
auto& priority_queue = GetPriorityQueue(kernel);
- /// We want to go over all cores, finding the highest priority thread and determining if
- /// scheduling is needed for that core.
+ // We want to go over all cores, finding the highest priority thread and determining if
+ // scheduling is needed for that core.
for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
if (top_thread != nullptr) {
- // If the thread has no waiters, we need to check if the process has a thread pinned.
- if (top_thread->GetNumKernelWaiters() == 0) {
- if (KProcess* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
- if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
- pinned != nullptr && pinned != top_thread) {
- // We prefer our parent's pinned thread if possible. However, we also don't
- // want to schedule un-runnable threads.
+ // We need to check if the thread's process has a pinned thread.
+ if (KProcess* parent = top_thread->GetOwnerProcess()) {
+ // Check that there's a pinned thread other than the current top thread.
+ if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
+ pinned != nullptr && pinned != top_thread) {
+ // We need to prefer threads with kernel waiters to the pinned thread.
+ if (top_thread->GetNumKernelWaiters() ==
+ 0 /* && top_thread != parent->GetExceptionThread() */) {
+ // If the pinned thread is runnable, use it.
if (pinned->GetRawState() == ThreadState::Runnable) {
top_thread = pinned;
} else {
@@ -129,7 +247,8 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
while (idle_cores != 0) {
- const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
+ const s32 core_id = static_cast<s32>(std::countr_zero(idle_cores));
+
if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
size_t num_candidates = 0;
@@ -150,7 +269,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// The suggested thread isn't bound to its core, so we can migrate it!
suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested);
-
top_threads[core_id] = suggested;
cores_needing_scheduling |=
kernel.Scheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]);
@@ -183,7 +301,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Perform the migration.
suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(candidate_core, suggested);
-
top_threads[core_id] = suggested;
cores_needing_scheduling |=
kernel.Scheduler(core_id).UpdateHighestPriorityThread(
@@ -200,24 +317,210 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
return cores_needing_scheduling;
}
+void KScheduler::SwitchThread(KThread* next_thread) {
+ KProcess* const cur_process = kernel.CurrentProcess();
+ KThread* const cur_thread = GetCurrentThreadPointer(kernel);
+
+ // We never want to schedule a null thread, so use the idle thread if we don't have a next.
+ if (next_thread == nullptr) {
+ next_thread = m_idle_thread;
+ }
+
+ if (next_thread->GetCurrentCore() != m_core_id) {
+ next_thread->SetCurrentCore(m_core_id);
+ }
+
+ // If we're not actually switching thread, there's nothing to do.
+ if (next_thread == cur_thread) {
+ return;
+ }
+
+ // Next thread is now known not to be nullptr, and must not be dispatchable.
+ ASSERT(next_thread->GetDisableDispatchCount() == 1);
+ ASSERT(!next_thread->IsDummyThread());
+
+ // Update the CPU time tracking variables.
+ const s64 prev_tick = m_last_context_switch_time;
+ const s64 cur_tick = kernel.System().CoreTiming().GetCPUTicks();
+ const s64 tick_diff = cur_tick - prev_tick;
+ cur_thread->AddCpuTime(m_core_id, tick_diff);
+ if (cur_process != nullptr) {
+ cur_process->UpdateCPUTimeTicks(tick_diff);
+ }
+ m_last_context_switch_time = cur_tick;
+
+ // Update our previous thread.
+ if (cur_process != nullptr) {
+ if (!cur_thread->IsTerminationRequested() && cur_thread->GetActiveCore() == m_core_id)
+ [[likely]] {
+ m_state.prev_thread = cur_thread;
+ } else {
+ m_state.prev_thread = nullptr;
+ }
+ }
+
+ // Switch the current process, if we're switching processes.
+ // if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
+ // KProcess::Switch(cur_process, next_process);
+ // }
+
+ // Set the new thread.
+ SetCurrentThread(kernel, next_thread);
+ m_current_thread = next_thread;
+
+ // Set the new Thread Local region.
+ // cpu::SwitchThreadLocalRegion(GetInteger(next_thread->GetThreadLocalRegionAddress()));
+}
+
+void KScheduler::ScheduleImpl() {
+ // First, clear the needs scheduling bool.
+ m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
+
+ // Load the appropriate thread pointers for scheduling.
+ KThread* const cur_thread{GetCurrentThreadPointer(kernel)};
+ KThread* highest_priority_thread{m_state.highest_priority_thread};
+
+ // Check whether there are runnable interrupt tasks.
+ if (m_state.interrupt_task_runnable) {
+ // The interrupt task is runnable.
+ // We want to switch to the interrupt task/idle thread.
+ highest_priority_thread = nullptr;
+ }
+
+ // If there aren't, we want to check if the highest priority thread is the same as the current
+ // thread.
+ if (highest_priority_thread == cur_thread) {
+ // If they're the same, then we can just return.
+ return;
+ }
+
+ // The highest priority thread is not the same as the current thread.
+ // Jump to the switcher and continue executing from there.
+ m_switch_cur_thread = cur_thread;
+ m_switch_highest_priority_thread = highest_priority_thread;
+ m_switch_from_schedule = true;
+ Common::Fiber::YieldTo(cur_thread->host_context, *m_switch_fiber);
+
+ // Returning from ScheduleImpl occurs after this thread has been scheduled again.
+}
+
+void KScheduler::ScheduleImplFiber() {
+ KThread* const cur_thread{m_switch_cur_thread};
+ KThread* highest_priority_thread{m_switch_highest_priority_thread};
+
+ // If we're not coming from scheduling (i.e., we came from SC preemption),
+ // we should restart the scheduling loop directly. Not accurate to HOS.
+ if (!m_switch_from_schedule) {
+ goto retry;
+ }
+
+ // Mark that we are not coming from scheduling anymore.
+ m_switch_from_schedule = false;
+
+ // Save the original thread context.
+ Unload(cur_thread);
+
+ // The current thread's context has been entirely taken care of.
+ // Now we want to loop until we successfully switch the thread context.
+ while (true) {
+ // We're starting to try to do the context switch.
+ // Check if the highest priority thread is null.
+ if (!highest_priority_thread) {
+ // The next thread is nullptr!
+
+ // Switch to the idle thread. Note: HOS treats idling as a special case for
+ // performance. This is not *required* for yuzu's purposes, and for singlecore
+ // compatibility, we can just move the logic that would go here into the execution
+ // of the idle thread. If we ever remove singlecore, we should implement this
+ // accurately to HOS.
+ highest_priority_thread = m_idle_thread;
+ }
+
+ // We want to try to lock the highest priority thread's context.
+ // Try to take it.
+ while (!highest_priority_thread->context_guard.try_lock()) {
+ // The highest priority thread's context is already locked.
+ // Check if we need scheduling. If we don't, we can retry directly.
+ if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
+ // If we do, another core is interfering, and we must start again.
+ goto retry;
+ }
+ }
+
+ // It's time to switch the thread.
+ // Switch to the highest priority thread.
+ SwitchThread(highest_priority_thread);
+
+ // Check if we need scheduling. If we do, then we can't complete the switch and should
+ // retry.
+ if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
+ // Our switch failed.
+ // We should unlock the thread context, and then retry.
+ highest_priority_thread->context_guard.unlock();
+ goto retry;
+ } else {
+ break;
+ }
+
+ retry:
+
+ // We failed to successfully do the context switch, and need to retry.
+ // Clear needs_scheduling.
+ m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
+
+ // Refresh the highest priority thread.
+ highest_priority_thread = m_state.highest_priority_thread;
+ }
+
+ // Reload the guest thread context.
+ Reload(highest_priority_thread);
+
+ // Reload the host thread.
+ Common::Fiber::YieldTo(m_switch_fiber, *highest_priority_thread->host_context);
+}
+
+void KScheduler::Unload(KThread* thread) {
+ auto& cpu_core = kernel.System().ArmInterface(m_core_id);
+ cpu_core.SaveContext(thread->GetContext32());
+ cpu_core.SaveContext(thread->GetContext64());
+ // Save the TPIDR_EL0 system register in case it was modified.
+ thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
+ cpu_core.ClearExclusiveState();
+
+ // Check if the thread is terminated by checking the DPC flags.
+ if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
+ // The thread isn't terminated, so we want to unlock it.
+ thread->context_guard.unlock();
+ }
+}
+
+void KScheduler::Reload(KThread* thread) {
+ auto& cpu_core = kernel.System().ArmInterface(m_core_id);
+ cpu_core.LoadContext(thread->GetContext32());
+ cpu_core.LoadContext(thread->GetContext64());
+ cpu_core.SetTlsAddress(thread->GetTLSAddress());
+ cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
+ cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints());
+ cpu_core.ClearExclusiveState();
+}
+
void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) {
// Get an atomic reference to the core scheduler's previous thread.
- std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
- static_assert(std::atomic_ref<KThread*>::is_always_lock_free);
+ auto& prev_thread{kernel.Scheduler(i).m_state.prev_thread};
// Atomically clear the previous thread if it's our target.
KThread* compare = thread;
- prev_thread.compare_exchange_strong(compare, nullptr);
+ prev_thread.compare_exchange_strong(compare, nullptr, std::memory_order_seq_cst);
}
}
void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Check if the state has changed, because if it hasn't there's nothing to do.
- const auto cur_state = thread->GetRawState();
+ const ThreadState cur_state = thread->GetRawState();
if (cur_state == old_state) {
return;
}
@@ -237,12 +540,12 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa
}
void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// If the thread is runnable, we want to change its priority in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
GetPriorityQueue(kernel).ChangePriority(old_priority,
- thread == kernel.GetCurrentEmuThread(), thread);
+ thread == GetCurrentThreadPointer(kernel), thread);
IncrementScheduledCount(thread);
SetSchedulerUpdateNeeded(kernel);
}
@@ -250,7 +553,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3
void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// If the thread is runnable, we want to change its affinity in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
@@ -260,15 +563,14 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread
}
}
-void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
- ASSERT(system.GlobalSchedulerContext().IsLocked());
+void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority) {
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Get a reference to the priority queue.
- auto& kernel = system.Kernel();
auto& priority_queue = GetPriorityQueue(kernel);
// Rotate the front of the queue to the end.
- KThread* top_thread = priority_queue.GetScheduledFront(cpu_core_id, priority);
+ KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
KThread* next_thread = nullptr;
if (top_thread != nullptr) {
next_thread = priority_queue.MoveToScheduledBack(top_thread);
@@ -280,7 +582,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
// While we have a suggested thread, try to migrate it!
{
- KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id, priority);
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
@@ -301,7 +603,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
// to the front of the queue.
if (top_on_suggested_core == nullptr ||
top_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) {
- suggested->SetActiveCore(cpu_core_id);
+ suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested, true);
IncrementScheduledCount(suggested);
break;
@@ -309,22 +611,21 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
}
// Get the next suggestion.
- suggested = priority_queue.GetSamePriorityNext(cpu_core_id, suggested);
+ suggested = priority_queue.GetSamePriorityNext(core_id, suggested);
}
}
// Now that we might have migrated a thread with the same priority, check if we can do better.
-
{
- KThread* best_thread = priority_queue.GetScheduledFront(cpu_core_id);
+ KThread* best_thread = priority_queue.GetScheduledFront(core_id);
if (best_thread == GetCurrentThreadPointer(kernel)) {
- best_thread = priority_queue.GetScheduledNext(cpu_core_id, best_thread);
+ best_thread = priority_queue.GetScheduledNext(core_id, best_thread);
}
// If the best thread we can choose has a priority the same or worse than ours, try to
// migrate a higher priority thread.
if (best_thread != nullptr && best_thread->GetPriority() >= priority) {
- KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id);
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// If the suggestion's priority is the same as ours, don't bother.
if (suggested->GetPriority() >= best_thread->GetPriority()) {
@@ -343,7 +644,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
if (top_on_suggested_core == nullptr ||
top_on_suggested_core->GetPriority() >=
HighestCoreMigrationAllowedPriority) {
- suggested->SetActiveCore(cpu_core_id);
+ suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested, true);
IncrementScheduledCount(suggested);
break;
@@ -351,7 +652,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
}
// Get the next suggestion.
- suggested = priority_queue.GetSuggestedNext(cpu_core_id, suggested);
+ suggested = priority_queue.GetSuggestedNext(core_id, suggested);
}
}
}
@@ -360,64 +661,6 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
SetSchedulerUpdateNeeded(kernel);
}
-bool KScheduler::CanSchedule(KernelCore& kernel) {
- return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1;
-}
-
-bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) {
- return kernel.GlobalSchedulerContext().scheduler_update_needed.load(std::memory_order_acquire);
-}
-
-void KScheduler::SetSchedulerUpdateNeeded(KernelCore& kernel) {
- kernel.GlobalSchedulerContext().scheduler_update_needed.store(true, std::memory_order_release);
-}
-
-void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
- kernel.GlobalSchedulerContext().scheduler_update_needed.store(false, std::memory_order_release);
-}
-
-void KScheduler::DisableScheduling(KernelCore& kernel) {
- // If we are shutting down the kernel, none of this is relevant anymore.
- if (kernel.IsShuttingDown()) {
- return;
- }
-
- ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
- GetCurrentThreadPointer(kernel)->DisableDispatch();
-}
-
-void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
- // If we are shutting down the kernel, none of this is relevant anymore.
- if (kernel.IsShuttingDown()) {
- return;
- }
-
- auto* current_thread = GetCurrentThreadPointer(kernel);
-
- ASSERT(current_thread->GetDisableDispatchCount() >= 1);
-
- if (current_thread->GetDisableDispatchCount() > 1) {
- current_thread->EnableDispatch();
- } else {
- RescheduleCores(kernel, cores_needing_scheduling);
- }
-
- // Special case to ensure dummy threads that are waiting block.
- current_thread->IfDummyThreadTryWait();
-}
-
-u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
- if (IsSchedulerUpdateNeeded(kernel)) {
- return UpdateHighestPriorityThreadsImpl(kernel);
- } else {
- return 0;
- }
-}
-
-KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) {
- return kernel.GlobalSchedulerContext().priority_queue;
-}
-
void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
@@ -437,7 +680,7 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -476,7 +719,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -496,7 +739,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
if (KThread* running_on_suggested_core =
(suggested_core >= 0)
- ? kernel.Scheduler(suggested_core).state.highest_priority_thread
+ ? kernel.Scheduler(suggested_core).m_state.highest_priority_thread
: nullptr;
running_on_suggested_core != suggested) {
// If the current thread's priority is higher than our suggestion's we prefer
@@ -564,7 +807,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -621,223 +864,19 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
}
}
-KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} {
- switch_fiber = std::make_shared<Common::Fiber>([this] { SwitchToCurrent(); });
- state.needs_scheduling.store(true);
- state.interrupt_task_thread_runnable = false;
- state.should_count_idle = false;
- state.idle_count = 0;
- state.idle_thread_stack = nullptr;
- state.highest_priority_thread = nullptr;
-}
-
-void KScheduler::Finalize() {
- if (idle_thread) {
- idle_thread->Close();
- idle_thread = nullptr;
- }
-}
-
-KScheduler::~KScheduler() {
- ASSERT(!idle_thread);
-}
-
-KThread* KScheduler::GetSchedulerCurrentThread() const {
- if (auto result = current_thread.load(); result) {
- return result;
+void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) {
+ if (const u64 core_mask = cores_needing_scheduling & ~(1ULL << m_core_id); core_mask != 0) {
+ RescheduleCores(kernel, core_mask);
}
- return idle_thread;
-}
-
-u64 KScheduler::GetLastContextSwitchTicks() const {
- return last_context_switch_time;
}
-void KScheduler::RescheduleCurrentCore() {
- ASSERT(GetCurrentThread(system.Kernel()).GetDisableDispatchCount() == 1);
-
- auto& phys_core = system.Kernel().PhysicalCore(core_id);
- if (phys_core.IsInterrupted()) {
- phys_core.ClearInterrupt();
- }
-
- guard.Lock();
- if (state.needs_scheduling.load()) {
- Schedule();
- } else {
- GetCurrentThread(system.Kernel()).EnableDispatch();
- guard.Unlock();
- }
-}
-
-void KScheduler::OnThreadStart() {
- SwitchContextStep2();
-}
-
-void KScheduler::Unload(KThread* thread) {
- ASSERT(thread);
-
- LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
-
- if (thread->IsCallingSvc()) {
- thread->ClearIsCallingSvc();
- }
-
- auto& physical_core = system.Kernel().PhysicalCore(core_id);
- if (!physical_core.IsInitialized()) {
- return;
- }
-
- Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
- cpu_core.SaveContext(thread->GetContext32());
- cpu_core.SaveContext(thread->GetContext64());
- // Save the TPIDR_EL0 system register in case it was modified.
- thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
-
- if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
- prev_thread = thread;
- } else {
- prev_thread = nullptr;
- }
-
- thread->context_guard.unlock();
-}
-
-void KScheduler::Reload(KThread* thread) {
- LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread->GetName());
-
- Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
- cpu_core.LoadContext(thread->GetContext32());
- cpu_core.LoadContext(thread->GetContext64());
- cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints());
- cpu_core.SetTlsAddress(thread->GetTLSAddress());
- cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
-}
-
-void KScheduler::SwitchContextStep2() {
- // Load context of new thread
- Reload(GetCurrentThreadPointer(system.Kernel()));
-
- RescheduleCurrentCore();
-}
-
-void KScheduler::Schedule() {
- ASSERT(GetCurrentThread(system.Kernel()).GetDisableDispatchCount() == 1);
- this->ScheduleImpl();
-}
-
-void KScheduler::ScheduleImpl() {
- KThread* previous_thread = GetCurrentThreadPointer(system.Kernel());
- KThread* next_thread = state.highest_priority_thread;
-
- state.needs_scheduling.store(false);
-
- // We never want to schedule a null thread, so use the idle thread if we don't have a next.
- if (next_thread == nullptr) {
- next_thread = idle_thread;
- }
-
- if (next_thread->GetCurrentCore() != core_id) {
- next_thread->SetCurrentCore(core_id);
- }
-
- // We never want to schedule a dummy thread, as these are only used by host threads for locking.
- if (next_thread->GetThreadType() == ThreadType::Dummy) {
- ASSERT_MSG(false, "Dummy threads should never be scheduled!");
- next_thread = idle_thread;
- }
-
- // If we're not actually switching thread, there's nothing to do.
- if (next_thread == current_thread.load()) {
- previous_thread->EnableDispatch();
- guard.Unlock();
- return;
- }
-
- // Update the CPU time tracking variables.
- KProcess* const previous_process = system.Kernel().CurrentProcess();
- UpdateLastContextSwitchTime(previous_thread, previous_process);
-
- // Save context for previous thread
- Unload(previous_thread);
-
- std::shared_ptr<Common::Fiber>* old_context;
- old_context = &previous_thread->GetHostContext();
-
- // Set the new thread.
- SetCurrentThread(system.Kernel(), next_thread);
- current_thread.store(next_thread);
-
- guard.Unlock();
-
- Common::Fiber::YieldTo(*old_context, *switch_fiber);
- /// When a thread wakes up, the scheduler may have changed to other in another core.
- auto& next_scheduler = *system.Kernel().CurrentScheduler();
- next_scheduler.SwitchContextStep2();
-}
-
-void KScheduler::SwitchToCurrent() {
- while (true) {
- {
- KScopedSpinLock lk{guard};
- current_thread.store(state.highest_priority_thread);
- state.needs_scheduling.store(false);
+void KScheduler::RescheduleCores(KernelCore& kernel, u64 core_mask) {
+ // Send IPI
+ for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
+ if (core_mask & (1ULL << i)) {
+ kernel.PhysicalCore(i).Interrupt();
}
- const auto is_switch_pending = [this] {
- KScopedSpinLock lk{guard};
- return state.needs_scheduling.load();
- };
- do {
- auto next_thread = current_thread.load();
- if (next_thread != nullptr) {
- const auto locked = next_thread->context_guard.try_lock();
- if (state.needs_scheduling.load()) {
- next_thread->context_guard.unlock();
- break;
- }
- if (next_thread->GetActiveCore() != core_id) {
- next_thread->context_guard.unlock();
- break;
- }
- if (!locked) {
- continue;
- }
- }
- auto thread = next_thread ? next_thread : idle_thread;
- SetCurrentThread(system.Kernel(), thread);
- Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
- } while (!is_switch_pending());
}
}
-void KScheduler::UpdateLastContextSwitchTime(KThread* thread, KProcess* process) {
- const u64 prev_switch_ticks = last_context_switch_time;
- const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
- const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
-
- if (thread != nullptr) {
- thread->AddCpuTime(core_id, update_ticks);
- }
-
- if (process != nullptr) {
- process->UpdateCPUTimeTicks(update_ticks);
- }
-
- last_context_switch_time = most_recent_switch_ticks;
-}
-
-void KScheduler::Initialize() {
- idle_thread = KThread::Create(system.Kernel());
- ASSERT(KThread::InitializeIdleThread(system, idle_thread, core_id).IsSuccess());
- idle_thread->SetName(fmt::format("IdleThread:{}", core_id));
- idle_thread->EnableDispatch();
-}
-
-KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)
- : KScopedLock(kernel.GlobalSchedulerContext().SchedulerLock()) {}
-
-KScopedSchedulerLock::~KScopedSchedulerLock() = default;
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 6a4760eca..534321d8d 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -11,6 +11,7 @@
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/k_scoped_lock.h"
#include "core/hle/kernel/k_spin_lock.h"
+#include "core/hle/kernel/k_thread.h"
namespace Common {
class Fiber;
@@ -23,184 +24,150 @@ class System;
namespace Kernel {
class KernelCore;
+class KInterruptTaskManager;
class KProcess;
-class SchedulerLock;
class KThread;
+class KScopedDisableDispatch;
+class KScopedSchedulerLock;
+class KScopedSchedulerLockAndSleep;
class KScheduler final {
public:
- explicit KScheduler(Core::System& system_, s32 core_id_);
- ~KScheduler();
-
- void Finalize();
+ YUZU_NON_COPYABLE(KScheduler);
+ YUZU_NON_MOVEABLE(KScheduler);
- /// Reschedules to the next available thread (call after current thread is suspended)
- void RescheduleCurrentCore();
+ using LockType = KAbstractSchedulerLock<KScheduler>;
- /// Reschedules cores pending reschedule, to be called on EnableScheduling.
- static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule);
+ explicit KScheduler(KernelCore& kernel);
+ ~KScheduler();
- /// The next two are for SingleCore Only.
- /// Unload current thread before preempting core.
+ void Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id);
+ void Activate();
+ void OnThreadStart();
void Unload(KThread* thread);
-
- /// Reload current thread after core preemption.
void Reload(KThread* thread);
- /// Gets the current running thread
- [[nodiscard]] KThread* GetSchedulerCurrentThread() const;
+ void SetInterruptTaskRunnable();
+ void RequestScheduleOnInterrupt();
+ void PreemptSingleCore();
- /// Gets the idle thread
- [[nodiscard]] KThread* GetIdleThread() const {
- return idle_thread;
+ u64 GetIdleCount() {
+ return m_state.idle_count;
}
- /// Returns true if the scheduler is idle
- [[nodiscard]] bool IsIdle() const {
- return GetSchedulerCurrentThread() == idle_thread;
+ KThread* GetIdleThread() const {
+ return m_idle_thread;
}
- /// Gets the timestamp for the last context switch in ticks.
- [[nodiscard]] u64 GetLastContextSwitchTicks() const;
-
- [[nodiscard]] bool ContextSwitchPending() const {
- return state.needs_scheduling.load(std::memory_order_relaxed);
+ bool IsIdle() const {
+ return m_current_thread.load() == m_idle_thread;
}
- void Initialize();
+ KThread* GetPreviousThread() const {
+ return m_state.prev_thread;
+ }
- void OnThreadStart();
+ KThread* GetSchedulerCurrentThread() const {
+ return m_current_thread.load();
+ }
- [[nodiscard]] std::shared_ptr<Common::Fiber>& ControlContext() {
- return switch_fiber;
+ s64 GetLastContextSwitchTime() const {
+ return m_last_context_switch_time;
}
- [[nodiscard]] const std::shared_ptr<Common::Fiber>& ControlContext() const {
- return switch_fiber;
+ // Static public API.
+ static bool CanSchedule(KernelCore& kernel) {
+ return GetCurrentThread(kernel).GetDisableDispatchCount() == 0;
+ }
+ static bool IsSchedulerLockedByCurrentThread(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().scheduler_lock.IsLockedByCurrentThread();
}
- [[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread);
+ static bool IsSchedulerUpdateNeeded(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().scheduler_update_needed;
+ }
+ static void SetSchedulerUpdateNeeded(KernelCore& kernel) {
+ kernel.GlobalSchedulerContext().scheduler_update_needed = true;
+ }
+ static void ClearSchedulerUpdateNeeded(KernelCore& kernel) {
+ kernel.GlobalSchedulerContext().scheduler_update_needed = false;
+ }
- /**
- * Takes a thread and moves it to the back of the it's priority list.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldWithoutCoreMigration(KernelCore& kernel);
+ static void DisableScheduling(KernelCore& kernel);
+ static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
- /**
- * Takes a thread and moves it to the back of the it's priority list.
- * Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or
- * a better priority than the next thread in the core.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldWithCoreMigration(KernelCore& kernel);
-
- /**
- * Takes a thread and moves it out of the scheduling queue.
- * and into the suggested queue. If no thread can be scheduled afterwards in that core,
- * a suggested thread is obtained instead.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldToAnyThread(KernelCore& kernel);
+ static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
static void ClearPreviousThread(KernelCore& kernel, KThread* thread);
- /// Notify the scheduler a thread's status has changed.
static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
-
- /// Notify the scheduler a thread's priority has changed.
static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority);
-
- /// Notify the scheduler a thread's core and/or affinity mask has changed.
static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core);
- static bool CanSchedule(KernelCore& kernel);
- static bool IsSchedulerUpdateNeeded(const KernelCore& kernel);
- static void SetSchedulerUpdateNeeded(KernelCore& kernel);
- static void ClearSchedulerUpdateNeeded(KernelCore& kernel);
- static void DisableScheduling(KernelCore& kernel);
- static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
- [[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
+ static void RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority);
+ static void RescheduleCores(KernelCore& kernel, u64 cores_needing_scheduling);
+
+ static void YieldWithoutCoreMigration(KernelCore& kernel);
+ static void YieldWithCoreMigration(KernelCore& kernel);
+ static void YieldToAnyThread(KernelCore& kernel);
private:
- friend class GlobalSchedulerContext;
-
- /**
- * Takes care of selecting the new scheduled threads in three steps:
- *
- * 1. First a thread is selected from the top of the priority queue. If no thread
- * is obtained then we move to step two, else we are done.
- *
- * 2. Second we try to get a suggested thread that's not assigned to any core or
- * that is not the top thread in that core.
- *
- * 3. Third is no suggested thread is found, we do a second pass and pick a running
- * thread in another core and swap it with its current thread.
- *
- * returns the cores needing scheduling.
- */
- [[nodiscard]] static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
-
- [[nodiscard]] static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel);
-
- void RotateScheduledQueue(s32 cpu_core_id, s32 priority);
+ // Static private API.
+ static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().priority_queue;
+ }
+ static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
- void Schedule();
+ static void RescheduleCurrentHLEThread(KernelCore& kernel);
- /// Switches the CPU's active thread context to that of the specified thread
+ // Instanced private API.
void ScheduleImpl();
+ void ScheduleImplFiber();
+ void SwitchThread(KThread* next_thread);
- /// When a thread wakes up, it must run this through it's new scheduler
- void SwitchContextStep2();
-
- /**
- * Called on every context switch to update the internal timestamp
- * This also updates the running time ticks for the given thread and
- * process using the following difference:
- *
- * ticks += most_recent_ticks - last_context_switch_ticks
- *
- * The internal tick timestamp for the scheduler is simply the
- * most recent tick count retrieved. No special arithmetic is
- * applied to it.
- */
- void UpdateLastContextSwitchTime(KThread* thread, KProcess* process);
-
- void SwitchToCurrent();
+ void Schedule();
+ void ScheduleOnInterrupt();
- KThread* prev_thread{};
- std::atomic<KThread*> current_thread{};
+ void RescheduleOtherCores(u64 cores_needing_scheduling);
+ void RescheduleCurrentCore();
+ void RescheduleCurrentCoreImpl();
- KThread* idle_thread{};
+ u64 UpdateHighestPriorityThread(KThread* thread);
- std::shared_ptr<Common::Fiber> switch_fiber{};
+private:
+ friend class KScopedDisableDispatch;
struct SchedulingState {
- std::atomic<bool> needs_scheduling{};
- bool interrupt_task_thread_runnable{};
- bool should_count_idle{};
- u64 idle_count{};
- KThread* highest_priority_thread{};
- void* idle_thread_stack{};
+ std::atomic<bool> needs_scheduling{false};
+ bool interrupt_task_runnable{false};
+ bool should_count_idle{false};
+ u64 idle_count{0};
+ KThread* highest_priority_thread{nullptr};
+ void* idle_thread_stack{nullptr};
+ std::atomic<KThread*> prev_thread{nullptr};
+ KInterruptTaskManager* interrupt_task_manager{nullptr};
};
- SchedulingState state;
-
- Core::System& system;
- u64 last_context_switch_time{};
- const s32 core_id;
-
- KSpinLock guard{};
+ KernelCore& kernel;
+ SchedulingState m_state;
+ bool m_is_active{false};
+ s32 m_core_id{0};
+ s64 m_last_context_switch_time{0};
+ KThread* m_idle_thread{nullptr};
+ std::atomic<KThread*> m_current_thread{nullptr};
+
+ std::shared_ptr<Common::Fiber> m_switch_fiber{};
+ KThread* m_switch_cur_thread{};
+ KThread* m_switch_highest_priority_thread{};
+ bool m_switch_from_schedule{};
};
-class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> {
+class KScopedSchedulerLock : public KScopedLock<KScheduler::LockType> {
public:
- explicit KScopedSchedulerLock(KernelCore& kernel);
- ~KScopedSchedulerLock();
+ explicit KScopedSchedulerLock(KernelCore& kernel)
+ : KScopedLock(kernel.GlobalSchedulerContext().scheduler_lock) {}
+ ~KScopedSchedulerLock() = default;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index 4fa256970..73314b45e 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -5,9 +5,11 @@
#include <atomic>
#include "common/assert.h"
+#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index b77735736..8ff1545b6 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/core.h"
diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h
index 2c1db0e70..34cb98456 100644
--- a/src/core/hle/kernel/k_shared_memory.h
+++ b/src/core/hle/kernel/k_shared_memory.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 50cb5fc90..174afc80d 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -258,7 +258,18 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_
}
Result KThread::InitializeDummyThread(KThread* thread) {
- return thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy);
+ // Initialize the thread.
+ R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy));
+
+ // Initialize emulation parameters.
+ thread->stack_parameters.disable_count = 0;
+
+ return ResultSuccess;
+}
+
+Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) {
+ return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
+ system.GetCpuManager().GetGuestActivateFunc());
}
Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
@@ -277,7 +288,7 @@ Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThr
KProcess* owner) {
system.Kernel().GlobalSchedulerContext().AddThread(thread);
return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner,
- ThreadType::User, system.GetCpuManager().GetGuestThreadStartFunc());
+ ThreadType::User, system.GetCpuManager().GetGuestThreadFunc());
}
void KThread::PostDestroy(uintptr_t arg) {
@@ -308,14 +319,20 @@ void KThread::Finalize() {
auto it = waiter_list.begin();
while (it != waiter_list.end()) {
- // Clear the lock owner
- it->SetLockOwner(nullptr);
+ // Get the thread.
+ KThread* const waiter = std::addressof(*it);
+
+ // The thread shouldn't be a kernel waiter.
+ ASSERT(!IsKernelAddressKey(waiter->GetAddressKey()));
+
+ // Clear the lock owner.
+ waiter->SetLockOwner(nullptr);
// Erase the waiter from our list.
it = waiter_list.erase(it);
// Cancel the thread's wait.
- it->CancelWait(ResultInvalidState, true);
+ waiter->CancelWait(ResultInvalidState, true);
}
}
@@ -1052,6 +1069,8 @@ void KThread::Exit() {
// Register the thread as a work task.
KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this);
}
+
+ UNREACHABLE_MSG("KThread::Exit() would return");
}
Result KThread::Sleep(s64 timeout) {
@@ -1087,6 +1106,8 @@ void KThread::IfDummyThreadTryWait() {
return;
}
+ ASSERT(!kernel.IsPhantomModeForSingleCore());
+
// Block until we are no longer waiting.
std::unique_lock lk(dummy_wait_lock);
dummy_wait_cv.wait(
@@ -1191,16 +1212,13 @@ KScopedDisableDispatch::~KScopedDisableDispatch() {
return;
}
- // Skip the reschedule if single-core, as dispatch tracking is disabled here.
- if (!Settings::values.use_multi_core.GetValue()) {
- return;
- }
-
if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
- auto scheduler = kernel.CurrentScheduler();
+ auto* scheduler = kernel.CurrentScheduler();
- if (scheduler) {
+ if (scheduler && !kernel.IsPhantomModeForSingleCore()) {
scheduler->RescheduleCurrentCore();
+ } else {
+ KScheduler::RescheduleCurrentHLEThread(kernel);
}
} else {
GetCurrentThread(kernel).EnableDispatch();
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 28cd7ecb0..9ee20208e 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -413,6 +413,9 @@ public:
[[nodiscard]] static Result InitializeDummyThread(KThread* thread);
+ [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread,
+ s32 virt_core);
+
[[nodiscard]] static Result InitializeIdleThread(Core::System& system, KThread* thread,
s32 virt_core);
@@ -480,39 +483,16 @@ public:
return per_core_priority_queue_entry[core];
}
- [[nodiscard]] bool IsKernelThread() const {
- return GetActiveCore() == 3;
- }
-
- [[nodiscard]] bool IsDispatchTrackingDisabled() const {
- return is_single_core || IsKernelThread();
- }
-
[[nodiscard]] s32 GetDisableDispatchCount() const {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return 1;
- }
-
return this->GetStackParameters().disable_count;
}
void DisableDispatch() {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return;
- }
-
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
this->GetStackParameters().disable_count++;
}
void EnableDispatch() {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return;
- }
-
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
this->GetStackParameters().disable_count--;
}
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 7307cf262..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"
@@ -64,8 +63,6 @@ struct KernelCore::Impl {
is_phantom_mode_for_singlecore = false;
- InitializePhysicalCores();
-
// Derive the initial memory layout from the emulated board
Init::InitializeSlabResourceCounts(kernel);
DeriveInitialMemoryLayout();
@@ -75,16 +72,16 @@ struct KernelCore::Impl {
InitializeSystemResourceLimit(kernel, system.CoreTiming());
InitializeMemoryLayout();
Init::InitializeKPageBufferSlabHeap(system);
- InitializeSchedulers();
InitializeShutdownThreads();
InitializePreemption(kernel);
+ InitializePhysicalCores();
RegisterHostThread();
}
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);
}
}
@@ -95,26 +92,16 @@ struct KernelCore::Impl {
process_list.clear();
- // Close all open server sessions and ports.
- std::unordered_set<KAutoObject*> server_objects_;
- {
- std::scoped_lock lk(server_objects_lock);
- server_objects_ = server_objects;
- server_objects.clear();
- }
- for (auto* server_object : server_objects_) {
- server_object->Close();
- }
-
- // Ensures all service threads gracefully shutdown.
- ClearServiceThreads();
+ CloseServices();
next_object_id = 0;
next_kernel_process_id = KProcess::InitialKIPIDMin;
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();
@@ -148,7 +135,6 @@ struct KernelCore::Impl {
shutdown_threads[core_id] = nullptr;
}
- schedulers[core_id]->Finalize();
schedulers[core_id].reset();
}
@@ -191,18 +177,41 @@ struct KernelCore::Impl {
global_object_list_container.reset();
}
+ void CloseServices() {
+ // Close all open server sessions and ports.
+ std::unordered_set<KAutoObject*> server_objects_;
+ {
+ std::scoped_lock lk(server_objects_lock);
+ server_objects_ = server_objects;
+ server_objects.clear();
+ }
+ for (auto* server_object : server_objects_) {
+ server_object->Close();
+ }
+
+ // Ensures all service threads gracefully shutdown.
+ ClearServiceThreads();
+ }
+
void InitializePhysicalCores() {
exclusive_monitor =
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
- schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i);
- cores.emplace_back(i, system, *schedulers[i], interrupts);
- }
- }
+ const s32 core{static_cast<s32>(i)};
- void InitializeSchedulers() {
- for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
- cores[i].Scheduler().Initialize();
+ schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel());
+ 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));
+ main_thread->SetCurrentCore(core);
+ ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess());
+
+ auto* idle_thread{Kernel::KThread::Create(system.Kernel())};
+ idle_thread->SetCurrentCore(core);
+ ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess());
+
+ schedulers[i]->Initialize(main_thread, idle_thread, core);
}
}
@@ -753,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};
@@ -777,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{};
@@ -813,6 +821,10 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
+void KernelCore::CloseServices() {
+ impl->CloseServices();
+}
+
const KResourceLimit* KernelCore::GetSystemResourceLimit() const {
return impl->system_resource_limit;
}
@@ -862,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 {
@@ -878,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() {
@@ -894,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;
}
@@ -927,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);
}
}
@@ -1101,10 +1098,11 @@ void KernelCore::Suspend(bool suspended) {
}
void KernelCore::ShutdownCores() {
+ KScopedSchedulerLock lk{*this};
+
for (auto* thread : impl->shutdown_threads) {
void(thread->Run());
}
- InterruptAllPhysicalCores();
}
bool KernelCore::IsMulticore() const {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index aa0ebaa02..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
@@ -109,6 +107,9 @@ public:
/// Clears all resources in use by the kernel instance.
void Shutdown();
+ /// Close all active services in use by the kernel instance.
+ void CloseServices();
+
/// Retrieves a shared pointer to the system resource limit instance.
const KResourceLimit* GetSystemResourceLimit() const;
@@ -180,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 a5b16ae2e..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.
@@ -43,27 +40,30 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
void PhysicalCore::Run() {
arm_interface->Run();
+ arm_interface->ClearExclusiveState();
}
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/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 8655506b0..27e5a805d 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -887,7 +887,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
const bool same_thread = current_thread == thread.GetPointerUnsafe();
- const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
+ const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
u64 out_ticks = 0;
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetCpuTime();
@@ -3026,11 +3026,6 @@ void Call(Core::System& system, u32 immediate) {
}
kernel.ExitSVCProfile();
-
- if (!thread->IsCallingSvc()) {
- auto* host_context = thread->GetHostContext().get();
- host_context->Rewind();
- }
}
} // namespace Kernel::Svc
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index aa9e5b89d..4de44cd06 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index b726ac27a..def105832 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -290,7 +290,7 @@ protected:
void Get(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
ProfileBase profile_base{};
- ProfileData data{};
+ UserData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 16};
@@ -373,18 +373,18 @@ protected:
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.RawString());
- if (user_data.size() < sizeof(ProfileData)) {
- LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
+ if (user_data.size() < sizeof(UserData)) {
+ LOG_ERROR(Service_ACC, "UserData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_BUFFER);
return;
}
- ProfileData data;
- std::memcpy(&data, user_data.data(), sizeof(ProfileData));
+ UserData data;
+ std::memcpy(&data, user_data.data(), sizeof(UserData));
if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) {
- LOG_ERROR(Service_ACC, "Failed to update profile data and base!");
+ LOG_ERROR(Service_ACC, "Failed to update user data and base!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_FAILED_SAVE_DATA);
return;
@@ -406,15 +406,15 @@ protected:
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.RawString());
- if (user_data.size() < sizeof(ProfileData)) {
- LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
+ if (user_data.size() < sizeof(UserData)) {
+ LOG_ERROR(Service_ACC, "UserData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_BUFFER);
return;
}
- ProfileData data;
- std::memcpy(&data, user_data.data(), sizeof(ProfileData));
+ UserData data;
+ std::memcpy(&data, user_data.data(), sizeof(UserData));
Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Write,
Common::FS::FileType::BinaryFile);
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 8118ead33..a58da4d5f 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -22,7 +22,7 @@ struct UserRaw {
UUID uuid2{};
u64 timestamp{};
ProfileUsername username{};
- ProfileData extra_data{};
+ UserData extra_data{};
};
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
@@ -263,7 +263,7 @@ UUID ProfileManager::GetLastOpenedUser() const {
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
- ProfileData& data) const {
+ UserData& data) const {
if (GetProfileBase(index, profile)) {
data = profiles[*index].data;
return true;
@@ -272,15 +272,14 @@ bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, Pro
}
/// Return the users profile base and the unknown arbitary data.
-bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
- ProfileData& data) const {
+bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, UserData& data) const {
const auto idx = GetUserIndex(uuid);
return GetProfileBaseAndData(idx, profile, data);
}
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
- ProfileData& data) const {
+ UserData& data) const {
return GetProfileBaseAndData(user.user_uuid, profile, data);
}
@@ -318,7 +317,7 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
}
bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
- const ProfileData& data_new) {
+ const UserData& data_new) {
const auto index = GetUserIndex(uuid);
if (index.has_value() && SetProfileBase(uuid, profile_new)) {
profiles[*index].data = data_new;
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 9940957f1..135f7d0d5 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -22,7 +22,7 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>;
/// Contains extra data related to a user.
/// TODO: RE this structure
-struct ProfileData {
+struct UserData {
INSERT_PADDING_WORDS_NOINIT(1);
u32 icon_id;
u8 bg_color_id;
@@ -30,7 +30,7 @@ struct ProfileData {
INSERT_PADDING_BYTES_NOINIT(0x10);
INSERT_PADDING_BYTES_NOINIT(0x60);
};
-static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size");
+static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
@@ -38,7 +38,7 @@ struct ProfileInfo {
Common::UUID user_uuid{};
ProfileUsername username{};
u64 creation_time{};
- ProfileData data{}; // TODO(ognik): Work out what this is
+ UserData data{}; // TODO(ognik): Work out what this is
bool is_open{};
};
@@ -74,10 +74,9 @@ public:
bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
- ProfileData& data) const;
- bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const;
- bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
- ProfileData& data) const;
+ UserData& data) const;
+ bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, UserData& data) const;
+ bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, UserData& data) const;
std::size_t GetUserCount() const;
std::size_t GetOpenUserCount() const;
bool UserExists(Common::UUID uuid) const;
@@ -93,7 +92,7 @@ public:
bool RemoveUser(Common::UUID uuid);
bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new);
bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
- const ProfileData& data_new);
+ const UserData& data_new);
private:
void ParseUserSaveFile();
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index d35644e73..118f226e4 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -5,7 +5,6 @@
#include <array>
#include <cinttypes>
#include <cstring>
-#include "audio_core/audio_renderer.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
@@ -238,6 +237,7 @@ IDebugFunctions::IDebugFunctions(Core::System& system_)
{130, nullptr, "FriendInvitationSetApplicationParameter"},
{131, nullptr, "FriendInvitationClearApplicationParameter"},
{132, nullptr, "FriendInvitationPushApplicationParameter"},
+ {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"},
{900, nullptr, "GetGrcProcessLaunchedSystemEvent"},
};
// clang-format on
@@ -285,7 +285,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
{64, nullptr, "SetInputDetectionSourceSet"},
- {65, nullptr, "ReportUserIsActive"},
+ {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"},
{66, nullptr, "GetCurrentIlluminance"},
{67, nullptr, "IsIlluminanceAvailable"},
{68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
@@ -517,6 +517,13 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
rb.Push<u32>(idle_time_detection_extension);
}
+void ISelfController::ReportUserIsActive(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
is_auto_sleep_disabled = rp.Pop<bool>();
@@ -635,6 +642,10 @@ void AppletMessageQueue::RequestExit() {
PushMessage(AppletMessage::Exit);
}
+void AppletMessageQueue::RequestResume() {
+ PushMessage(AppletMessage::Resume);
+}
+
void AppletMessageQueue::FocusStateChanged() {
PushMessage(AppletMessage::FocusStateChanged);
}
@@ -1310,6 +1321,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
{34, nullptr, "SelectApplicationLicense"},
{35, nullptr, "GetDeviceSaveDataSizeMax"},
+ {36, nullptr, "GetLimitedApplicationLicense"},
+ {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 988ead215..bb75c6281 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -90,6 +90,7 @@ public:
AppletMessage PopMessage();
std::size_t GetMessageCount() const;
void RequestExit();
+ void RequestResume();
void FocusStateChanged();
void OperationModeChanged();
@@ -174,6 +175,7 @@ private:
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
+ void ReportUserIsActive(Kernel::HLERequestContext& ctx);
void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx);
void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index faa092957..c18236045 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -536,6 +536,8 @@ void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
.sub_text{std::move(sub_text)},
.guide_text{std::move(guide_text)},
.initial_text{initial_text},
+ .left_optional_symbol_key{swkbd_config_common.left_optional_symbol_key},
+ .right_optional_symbol_key{swkbd_config_common.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
@@ -591,6 +593,8 @@ void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
.sub_text{},
.guide_text{},
.initial_text{current_text},
+ .left_optional_symbol_key{appear_arg.left_optional_symbol_key},
+ .right_optional_symbol_key{appear_arg.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
@@ -632,6 +636,8 @@ void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
.sub_text{},
.guide_text{},
.initial_text{current_text},
+ .left_optional_symbol_key{appear_arg.left_optional_symbol_key},
+ .right_optional_symbol_key{appear_arg.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 18d3ae682..48a9a73a0 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -1,68 +1,211 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "audio_core/in/audio_in_system.h"
+#include "audio_core/renderer/audio_device.h"
+#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/audio/audin_u.h"
namespace Service::Audio {
+using namespace AudioCore::AudioIn;
-IAudioIn::IAudioIn(Core::System& system_)
- : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetAudioInState"},
- {1, &IAudioIn::Start, "Start"},
- {2, nullptr, "Stop"},
- {3, nullptr, "AppendAudioInBuffer"},
- {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
- {5, nullptr, "GetReleasedAudioInBuffer"},
- {6, nullptr, "ContainsAudioInBuffer"},
- {7, nullptr, "AppendUacInBuffer"},
- {8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"},
- {9, nullptr, "GetReleasedAudioInBuffersAuto"},
- {10, nullptr, "AppendUacInBufferAuto"},
- {11, nullptr, "GetAudioInBufferCount"},
- {12, nullptr, "SetDeviceGain"},
- {13, nullptr, "GetDeviceGain"},
- {14, nullptr, "FlushAudioInBuffers"},
- };
- // clang-format on
+class IAudioIn final : public ServiceFramework<IAudioIn> {
+public:
+ explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
+ std::string& device_name, const AudioInParameter& in_params, u32 handle,
+ u64 applet_resource_user_id)
+ : ServiceFramework{system_, "IAudioIn"},
+ service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
+ impl{std::make_shared<In>(system_, manager, event, session_id)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IAudioIn::GetAudioInState, "GetAudioInState"},
+ {1, &IAudioIn::Start, "Start"},
+ {2, &IAudioIn::Stop, "Stop"},
+ {3, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBuffer"},
+ {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
+ {5, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffer"},
+ {6, &IAudioIn::ContainsAudioInBuffer, "ContainsAudioInBuffer"},
+ {7, &IAudioIn::AppendAudioInBuffer, "AppendUacInBuffer"},
+ {8, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBufferAuto"},
+ {9, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffersAuto"},
+ {10, &IAudioIn::AppendAudioInBuffer, "AppendUacInBufferAuto"},
+ {11, &IAudioIn::GetAudioInBufferCount, "GetAudioInBufferCount"},
+ {12, &IAudioIn::SetDeviceGain, "SetDeviceGain"},
+ {13, &IAudioIn::GetDeviceGain, "GetDeviceGain"},
+ {14, &IAudioIn::FlushAudioInBuffers, "FlushAudioInBuffers"},
+ };
+ // clang-format on
- RegisterHandlers(functions);
+ RegisterHandlers(functions);
- buffer_event = service_context.CreateEvent("IAudioIn:BufferEvent");
-}
+ if (impl->GetSystem()
+ .Initialize(device_name, in_params, handle, applet_resource_user_id)
+ .IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!");
+ }
+ }
-IAudioIn::~IAudioIn() {
- service_context.CloseEvent(buffer_event);
-}
+ ~IAudioIn() override {
+ impl->Free();
+ service_context.CloseEvent(event);
+ }
-void IAudioIn::Start(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ [[nodiscard]] std::shared_ptr<In> GetImpl() {
+ return impl;
+ }
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
+private:
+ void GetAudioInState(Kernel::HLERequestContext& ctx) {
+ const auto state = static_cast<u32>(impl->GetState());
-void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called. State={}", state);
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
-}
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(state);
+ }
-void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ void Start(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
+ auto result = impl->StartSystem();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void Stop(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto result = impl->StopSystem();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void AppendAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ u64 tag = rp.PopRaw<u64>();
-AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
+ const auto in_buffer_size{ctx.GetReadBufferSize()};
+ if (in_buffer_size < sizeof(AudioInBuffer)) {
+ LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!");
+ }
+
+ const auto& in_buffer = ctx.ReadBuffer();
+ AudioInBuffer buffer{};
+ std::memcpy(&buffer, in_buffer.data(), sizeof(AudioInBuffer));
+
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
+
+ auto result = impl->AppendBuffer(buffer, tag);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto& buffer_event = impl->GetBufferEvent();
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(buffer_event);
+ }
+
+ void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
+ std::vector<u64> released_buffers(write_buffer_size, 0);
+
+ auto count = impl->GetReleasedBuffers(released_buffers);
+
+ [[maybe_unused]] std::string tags{};
+ for (u32 i = 0; i < count; i++) {
+ tags += fmt::format("{:08X}, ", released_buffers[i]);
+ }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
+ tags);
+
+ ctx.WriteBuffer(released_buffers);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(count);
+ }
+
+ void ContainsAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const u64 tag{rp.Pop<u64>()};
+ const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
+
+ LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(buffer_queued);
+ }
+
+ void GetAudioInBufferCount(Kernel::HLERequestContext& ctx) {
+ const auto buffer_count = impl->GetBufferCount();
+
+ LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ rb.Push(ResultSuccess);
+ rb.Push(buffer_count);
+ }
+
+ void SetDeviceGain(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto volume{rp.Pop<f32>()};
+ LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
+
+ impl->SetVolume(volume);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void GetDeviceGain(Kernel::HLERequestContext& ctx) {
+ auto volume{impl->GetVolume()};
+
+ LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(volume);
+ }
+
+ void FlushAudioInBuffers(Kernel::HLERequestContext& ctx) {
+ bool flushed{impl->FlushAudioInBuffers()};
+
+ LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(flushed);
+ }
+
+ KernelHelpers::ServiceContext service_context;
+ Kernel::KEvent* event;
+ std::shared_ptr<AudioCore::AudioIn::In> impl;
+};
+
+AudInU::AudInU(Core::System& system_)
+ : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew},
+ service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
+ system_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &AudInU::ListAudioIns, "ListAudioIns"},
@@ -80,59 +223,152 @@ AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
AudInU::~AudInU() = default;
void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
+
LOG_DEBUG(Service_Audio, "called");
- const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioInDeviceName);
- const std::size_t device_count = std::min(count, audio_device_names.size());
- std::vector<AudioInDeviceName> device_names;
- device_names.reserve(device_count);
+ const auto write_count =
+ static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
+ std::vector<AudioDevice::AudioDeviceName> device_names{};
- for (std::size_t i = 0; i < device_count; i++) {
- const auto& device_name = audio_device_names[i];
- auto& entry = device_names.emplace_back();
- device_name.copy(entry.data(), device_name.size());
+ u32 out_count{0};
+ if (write_count > 0) {
+ out_count = impl->GetDeviceNames(device_names, write_count, false);
+ ctx.WriteBuffer(device_names);
}
- ctx.WriteBuffer(device_names);
-
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(device_names.size()));
+ rb.Push(out_count);
}
void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
+
LOG_DEBUG(Service_Audio, "called");
- constexpr u32 device_count = 0;
- // Since we don't actually use any other audio input devices, we return 0 devices. Filtered
- // device listing just omits the default input device
+ const auto write_count =
+ static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
+ std::vector<AudioDevice::AudioDeviceName> device_names{};
+
+ u32 out_count{0};
+ if (write_count > 0) {
+ out_count = impl->GetDeviceNames(device_names, write_count, true);
+ ctx.WriteBuffer(device_names);
+ }
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(device_count));
+ rb.Push(out_count);
}
-void AudInU::OpenInOutImpl(Kernel::HLERequestContext& ctx) {
- AudInOutParams params{};
- params.channel_count = 2;
- params.sample_format = SampleFormat::PCM16;
- params.sample_rate = 48000;
- params.state = State::Started;
+void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto in_params{rp.PopRaw<AudioInParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
+ const auto device_name_data{ctx.ReadBuffer()};
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
+
+ std::scoped_lock l{impl->mutex};
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
+ }
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+ impl->sessions[new_session_id] = audio_in->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushRaw<AudInOutParams>(params);
- rb.PushIpcInterface<IAudioIn>(system);
-}
-void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
- OpenInOutImpl(ctx);
+ std::string out_name{out_system.GetName()};
+ ctx.WriteBuffer(out_name);
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioInParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioIn>(audio_in);
}
void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
- OpenInOutImpl(ctx);
+ IPC::RequestParser rp{ctx};
+ auto protocol_specified{rp.PopRaw<u64>()};
+ auto in_params{rp.PopRaw<AudioInParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
+ const auto device_name_data{ctx.ReadBuffer()};
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
+
+ std::scoped_lock l{impl->mutex};
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
+ }
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+ impl->sessions[new_session_id] = audio_in->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
+
+ IPC::ResponseBuilder rb{ctx, 6, 0, 1};
+
+ std::string out_name{out_system.GetName()};
+ if (protocol_specified == 0) {
+ if (out_system.IsUac()) {
+ out_name = "UacIn";
+ } else {
+ out_name = "DeviceIn";
+ }
+ }
+
+ ctx.WriteBuffer(out_name);
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioInParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioIn>(audio_in);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h
index 2bfa38ecc..b45fda78a 100644
--- a/src/core/hle/service/audio/audin_u.h
+++ b/src/core/hle/service/audio/audin_u.h
@@ -3,6 +3,8 @@
#pragma once
+#include "audio_core/audio_in_manager.h"
+#include "audio_core/in/audio_in.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
@@ -14,22 +16,12 @@ namespace Kernel {
class HLERequestContext;
}
-namespace Service::Audio {
-
-class IAudioIn final : public ServiceFramework<IAudioIn> {
-public:
- explicit IAudioIn(Core::System& system_);
- ~IAudioIn() override;
-
-private:
- void Start(Kernel::HLERequestContext& ctx);
- void RegisterBufferEvent(Kernel::HLERequestContext& ctx);
- void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx);
-
- KernelHelpers::ServiceContext service_context;
+namespace AudioCore::AudioOut {
+class Manager;
+class In;
+} // namespace AudioCore::AudioOut
- Kernel::KEvent* buffer_event;
-};
+namespace Service::Audio {
class AudInU final : public ServiceFramework<AudInU> {
public:
@@ -37,33 +29,14 @@ public:
~AudInU() override;
private:
- enum class SampleFormat : u32_le {
- PCM16 = 2,
- };
-
- enum class State : u32_le {
- Started = 0,
- Stopped = 1,
- };
-
- struct AudInOutParams {
- u32_le sample_rate{};
- u32_le channel_count{};
- SampleFormat sample_format{};
- State state{};
- };
- static_assert(sizeof(AudInOutParams) == 0x10, "AudInOutParams is an invalid size");
-
- using AudioInDeviceName = std::array<char, 256>;
- static constexpr std::array<std::string_view, 1> audio_device_names{{
- "BuiltInHeadset",
- }};
-
void ListAudioIns(Kernel::HLERequestContext& ctx);
void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx);
void OpenInOutImpl(Kernel::HLERequestContext& ctx);
void OpenAudioIn(Kernel::HLERequestContext& ctx);
void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx);
+
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioCore::AudioIn::Manager> impl;
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index b0dad6053..a44dd842a 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -5,56 +5,43 @@
#include <cstring>
#include <vector>
-#include "audio_core/audio_out.h"
-#include "audio_core/codec.h"
+#include "audio_core/out/audio_out_system.h"
+#include "audio_core/renderer/audio_device.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/audio/audout_u.h"
#include "core/hle/service/audio/errors.h"
-#include "core/hle/service/kernel_helpers.h"
#include "core/memory.h"
namespace Service::Audio {
-
-constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
-constexpr int DefaultSampleRate{48000};
-
-struct AudoutParams {
- s32_le sample_rate;
- u16_le channel_count;
- INSERT_PADDING_BYTES_NOINIT(2);
-};
-static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
-
-enum class AudioState : u32 {
- Started,
- Stopped,
-};
+using namespace AudioCore::AudioOut;
class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
- explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
- AudioCore::AudioOut& audio_core_, std::string&& device_name_,
- std::string&& unique_name)
+ explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
+ size_t session_id, std::string& device_name,
+ const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
: ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
- audio_core{audio_core_}, device_name{std::move(device_name_)},
- audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_,
- "IAudioOut"} {
+ service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(
+ "AudioOutEvent")},
+ impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
+
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
- {1, &IAudioOut::StartAudioOut, "Start"},
- {2, &IAudioOut::StopAudioOut, "Stop"},
- {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
+ {1, &IAudioOut::Start, "Start"},
+ {2, &IAudioOut::Stop, "Stop"},
+ {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"},
{4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
- {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"},
+ {5, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffers"},
{6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
- {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
- {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
+ {7, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBufferAuto"},
+ {8, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffersAuto"},
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"},
{11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
@@ -64,241 +51,263 @@ public:
// clang-format on
RegisterHandlers(functions);
- // This is the event handle used to check if the audio buffer was released
- buffer_event = service_context.CreateEvent("IAudioOutBufferReleased");
-
- stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
- audio_params.channel_count, std::move(unique_name), [this] {
- const auto guard = LockService();
- buffer_event->GetWritableEvent().Signal();
- });
+ if (impl->GetSystem()
+ .Initialize(device_name, in_params, handle, applet_resource_user_id)
+ .IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
+ }
}
~IAudioOut() override {
- service_context.CloseEvent(buffer_event);
+ impl->Free();
+ service_context.CloseEvent(event);
}
-private:
- struct AudioBuffer {
- u64_le next;
- u64_le buffer;
- u64_le buffer_capacity;
- u64_le buffer_size;
- u64_le offset;
- };
- static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
+ [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() {
+ return impl;
+ }
+private:
void GetAudioOutState(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto state = static_cast<u32>(impl->GetState());
+
+ LOG_DEBUG(Service_Audio, "called. State={}", state);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
+ rb.Push(state);
}
- void StartAudioOut(Kernel::HLERequestContext& ctx) {
+ void Start(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
- if (stream->IsPlaying()) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_OPERATION_FAILED);
- return;
- }
-
- audio_core.StartStream(stream);
+ auto result = impl->StartSystem();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
- void StopAudioOut(Kernel::HLERequestContext& ctx) {
+ void Stop(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
- if (stream->IsPlaying()) {
- audio_core.StopStream(stream);
- }
+ auto result = impl->StopSystem();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
- void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
- }
-
- void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
+ void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
+ u64 tag = rp.PopRaw<u64>();
- const auto& input_buffer{ctx.ReadBuffer()};
- ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer),
- "AudioBuffer input is an invalid size!");
- AudioBuffer audio_buffer{};
- std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
- const u64 tag{rp.Pop<u64>()};
+ const auto in_buffer_size{ctx.GetReadBufferSize()};
+ if (in_buffer_size < sizeof(AudioOutBuffer)) {
+ LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!");
+ }
- std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
- main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
+ const auto& in_buffer = ctx.ReadBuffer();
+ AudioOutBuffer buffer{};
+ std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer));
- if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_BUFFER_COUNT_EXCEEDED);
- return;
- }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
+
+ auto result = impl->AppendBuffer(buffer, tag);
IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto& buffer_event = impl->GetBufferEvent();
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
+ rb.PushCopyObjects(buffer_event);
}
- void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
+ void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) {
+ auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
+ std::vector<u64> released_buffers(write_buffer_size, 0);
- const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
- const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
+ auto count = impl->GetReleasedBuffers(released_buffers);
- std::vector<u64> tags{released_buffers};
- tags.resize(max_count);
- ctx.WriteBuffer(tags);
+ [[maybe_unused]] std::string tags{};
+ for (u32 i = 0; i < count; i++) {
+ tags += fmt::format("{:08X}, ", released_buffers[i]);
+ }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
+ tags);
+ ctx.WriteBuffer(released_buffers);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(released_buffers.size()));
+ rb.Push(count);
}
void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
IPC::RequestParser rp{ctx};
+
const u64 tag{rp.Pop<u64>()};
+ const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
+
+ LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->ContainsBuffer(tag));
+ rb.Push(buffer_queued);
}
void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto buffer_count = impl->GetBufferCount();
+
+ LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
IPC::ResponseBuilder rb{ctx, 3};
+
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(stream->GetQueueSize()));
+ rb.Push(buffer_count);
}
void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto samples_played = impl->GetPlayedSampleCount();
+
+ LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played);
IPC::ResponseBuilder rb{ctx, 4};
+
rb.Push(ResultSuccess);
- rb.Push(stream->GetPlayedSampleCount());
+ rb.Push(samples_played);
}
void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ bool flushed{impl->FlushAudioOutBuffers()};
+
+ LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->Flush());
+ rb.Push(flushed);
}
void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const float volume = rp.Pop<float>();
- LOG_DEBUG(Service_Audio, "called, volume={}", volume);
+ const auto volume = rp.Pop<f32>();
+
+ LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
- stream->SetVolume(volume);
+ impl->SetVolume(volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto volume = impl->GetVolume();
+
+ LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->GetVolume());
+ rb.Push(volume);
}
- AudioCore::AudioOut& audio_core;
- AudioCore::StreamPtr stream;
- std::string device_name;
-
- [[maybe_unused]] AudoutParams audio_params{};
-
- Core::Memory::Memory& main_memory;
-
KernelHelpers::ServiceContext service_context;
-
- /// This is the event handle used to check if the audio buffer was released
- Kernel::KEvent* buffer_event;
+ Kernel::KEvent* event;
+ std::shared_ptr<AudioCore::AudioOut::Out> impl;
};
-AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} {
+AudOutU::AudOutU(Core::System& system_)
+ : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew},
+ service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>(
+ system_)} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
- {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
- {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
- {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"},
+ {0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
+ {1, &AudOutU::OpenAudioOut, "OpenAudioOut"},
+ {2, &AudOutU::ListAudioOuts, "ListAudioOutsAuto"},
+ {3, &AudOutU::OpenAudioOut, "OpenAudioOutAuto"},
};
// clang-format on
RegisterHandlers(functions);
- audio_core = std::make_unique<AudioCore::AudioOut>();
}
AudOutU::~AudOutU() = default;
-void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
- ctx.WriteBuffer(DefaultDevice);
+ std::scoped_lock l{impl->mutex};
+
+ 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"));
+ LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut");
+ } else {
+ LOG_DEBUG(Service_Audio, "called. Empty buffer passed in.");
+ }
+
+ ctx.WriteBuffer(device_names);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(1); // Amount of audio devices
+ rb.Push<u32>(static_cast<u32>(device_names.size()));
}
-void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
+void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto in_params{rp.PopRaw<AudioOutParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
const auto device_name_data{ctx.ReadBuffer()};
- std::string device_name;
- if (device_name_data[0] != '\0') {
- device_name.assign(device_name_data.begin(), device_name_data.end());
- } else {
- device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
- }
- ctx.WriteBuffer(device_name);
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
- IPC::RequestParser rp{ctx};
- auto params{rp.PopRaw<AudoutParams>()};
- if (params.channel_count <= 2) {
- // Mono does not exist for audout
- params.channel_count = 2;
- } else {
- params.channel_count = 6;
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
}
- if (!params.sample_rate) {
- params.sample_rate = DefaultSampleRate;
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
}
- std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
- auto audio_out_interface = std::make_shared<IAudioOut>(
- system, params, *audio_core, std::move(device_name), std::move(unique_name));
+ LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+
+ impl->sessions[new_session_id] = audio_out->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioOutParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
- rb.Push(ResultSuccess);
- rb.Push<u32>(DefaultSampleRate);
- rb.Push<u32>(params.channel_count);
- rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
- rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
- rb.PushIpcInterface<IAudioOut>(audio_out_interface);
- audio_out_interfaces.push_back(std::move(audio_out_interface));
+ ctx.WriteBuffer(out_system.GetName());
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioOutParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioOut>(audio_out);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index d82004c2e..fdc0ee754 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -3,13 +3,11 @@
#pragma once
-#include <vector>
+#include "audio_core/audio_out_manager.h"
+#include "audio_core/out/audio_out.h"
+#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
-namespace AudioCore {
-class AudioOut;
-}
-
namespace Core {
class System;
}
@@ -18,6 +16,11 @@ namespace Kernel {
class HLERequestContext;
}
+namespace AudioCore::AudioOut {
+class Manager;
+class Out;
+} // namespace AudioCore::AudioOut
+
namespace Service::Audio {
class IAudioOut;
@@ -28,11 +31,11 @@ public:
~AudOutU() override;
private:
- void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
- void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
+ void ListAudioOuts(Kernel::HLERequestContext& ctx);
+ void OpenAudioOut(Kernel::HLERequestContext& ctx);
- std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
- std::unique_ptr<AudioCore::AudioOut> audio_core;
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioCore::AudioOut::Manager> impl;
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 2ce63c004..bc69117c6 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -4,7 +4,12 @@
#include <array>
#include <memory>
-#include "audio_core/audio_renderer.h"
+#include "audio_core/audio_core.h"
+#include "audio_core/common/audio_renderer_parameter.h"
+#include "audio_core/common/feature_support.h"
+#include "audio_core/renderer/audio_device.h"
+#include "audio_core/renderer/audio_renderer.h"
+#include "audio_core/renderer/voice/voice_info.h"
#include "common/alignment.h"
#include "common/bit_util.h"
#include "common/common_funcs.h"
@@ -13,91 +18,127 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/audio/audren_u.h"
#include "core/hle/service/audio/errors.h"
+#include "core/memory.h"
+
+using namespace AudioCore::AudioRenderer;
namespace Service::Audio {
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
- explicit IAudioRenderer(Core::System& system_,
- const AudioCommon::AudioRendererParameter& audren_params,
- const std::size_t instance_number)
+ explicit IAudioRenderer(Core::System& system_, Manager& manager_,
+ AudioCore::AudioRendererParameterInternal& params,
+ Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
+ u32 process_handle, u64 applet_resource_user_id, s32 session_id)
: ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
- service_context{system_, "IAudioRenderer"} {
+ service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent(
+ "IAudioRendererEvent")},
+ manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
{3, &IAudioRenderer::GetState, "GetState"},
- {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
+ {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
{5, &IAudioRenderer::Start, "Start"},
{6, &IAudioRenderer::Stop, "Stop"},
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
{8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
- {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
- {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"},
+ {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"},
+ {11, nullptr, "ExecuteAudioRendererRendering"},
};
// clang-format on
RegisterHandlers(functions);
- system_event = service_context.CreateEvent("IAudioRenderer:SystemEvent");
- renderer = std::make_unique<AudioCore::AudioRenderer>(
- system.CoreTiming(), system.Memory(), audren_params,
- [this]() {
- const auto guard = LockService();
- system_event->GetWritableEvent().Signal();
- },
- instance_number);
+ impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle,
+ applet_resource_user_id, session_id);
}
~IAudioRenderer() override {
- service_context.CloseEvent(system_event);
+ impl->Finalize();
+ service_context.CloseEvent(rendered_event);
}
private:
void GetSampleRate(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto sample_rate{impl->GetSystem().GetSampleRate()};
+
+ LOG_DEBUG(Service_Audio, "called. Sample rate {}", sample_rate);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetSampleRate());
+ rb.Push(sample_rate);
}
void GetSampleCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto sample_count{impl->GetSystem().GetSampleCount()};
+
+ LOG_DEBUG(Service_Audio, "called. Sample count {}", sample_count);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetSampleCount());
+ rb.Push(sample_count);
}
void GetState(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const u32 state{!impl->GetSystem().IsActive()};
+
+ LOG_DEBUG(Service_Audio, "called, state {}", state);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
+ rb.Push(state);
}
void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+ const auto buffer_count{impl->GetSystem().GetMixBufferCount()};
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetMixBufferCount());
+ rb.Push(buffer_count);
}
- void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "(STUBBED) called");
+ void RequestUpdate(Kernel::HLERequestContext& ctx) {
+ LOG_TRACE(Service_Audio, "called");
+
+ std::vector<u8> input{ctx.ReadBuffer(0)};
+
+ // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
+ // checking size 0. Performance size is 0 for most games.
+
+ 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);
+ }
- std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0);
- auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
+ auto result = impl->RequestUpdate(input, performance, output);
if (result.IsSuccess()) {
- ctx.WriteBuffer(output_params);
+ 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);
}
IPC::ResponseBuilder rb{ctx, 2};
@@ -105,38 +146,45 @@ private:
}
void Start(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
- const auto result = renderer->Start();
+ impl->Start();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void Stop(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
- const auto result = renderer->Stop();
+ impl->Stop();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
+
+ if (impl->GetSystem().GetExecutionMode() == AudioCore::ExecutionMode::Manual) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_NOT_SUPPORTED);
+ return;
+ }
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(system_event->GetReadableEvent());
+ rb.PushCopyObjects(rendered_event->GetReadableEvent());
}
void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
IPC::RequestParser rp{ctx};
- rendering_time_limit_percent = rp.Pop<u32>();
- LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
- rendering_time_limit_percent);
+ auto limit = rp.PopRaw<u32>();
- ASSERT(rendering_time_limit_percent <= 100);
+ auto& system_ = impl->GetSystem();
+ system_.SetRenderingTimeLimit(limit);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -145,34 +193,34 @@ private:
void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+ auto& system_ = impl->GetSystem();
+ auto time = system_.GetRenderingTimeLimit();
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(rendering_time_limit_percent);
+ rb.Push(time);
}
void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
-
- // This service command currently only reports an unsupported operation
- // error code, or aborts. Given that, we just always return an error
- // code in this case.
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_NOT_SUPPORTED);
}
KernelHelpers::ServiceContext service_context;
-
- Kernel::KEvent* system_event;
- std::unique_ptr<AudioCore::AudioRenderer> renderer;
- u32 rendering_time_limit_percent = 100;
+ Kernel::KEvent* rendered_event;
+ Manager& manager;
+ std::unique_ptr<Renderer> impl;
};
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
+
public:
- explicit IAudioDevice(Core::System& system_, Kernel::KEvent* buffer_event_, u32_le revision_)
- : ServiceFramework{system_, "IAudioDevice"}, buffer_event{buffer_event_}, revision{
- revision_} {
+ explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
+ u32 device_num)
+ : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew},
+ service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>(
+ system_, applet_resource_user_id,
+ revision)},
+ event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
static const FunctionInfo functions[] = {
{0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
{1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
@@ -186,54 +234,45 @@ public:
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
{11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"},
{12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
- {13, nullptr, "GetActiveAudioOutputDeviceName"},
- {14, nullptr, "ListAudioOutputDeviceName"},
+ {13, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioOutputDeviceName"},
+ {14, &IAudioDevice::ListAudioOutputDeviceName, "ListAudioOutputDeviceName"},
};
RegisterHandlers(functions);
+
+ event->GetWritableEvent().Signal();
}
-private:
- using AudioDeviceName = std::array<char, 256>;
- static constexpr std::array<std::string_view, 4> audio_device_names{{
- "AudioStereoJackOutput",
- "AudioBuiltInSpeakerOutput",
- "AudioTvOutput",
- "AudioUsbDeviceOutput",
- }};
- enum class DeviceType {
- AHUBHeadphones,
- AHUBSpeakers,
- HDA,
- USBOutput,
- };
+ ~IAudioDevice() override {
+ service_context.CloseEvent(event);
+ }
+private:
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
- const bool usb_output_supported =
- IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision);
- const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName);
+ std::vector<AudioDevice::AudioDeviceName> out_names{};
- std::vector<AudioDeviceName> name_buffer;
- name_buffer.reserve(audio_device_names.size());
+ u32 out_count = impl->ListAudioDeviceName(out_names, in_count);
- for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) {
- const auto type = static_cast<DeviceType>(i);
-
- if (!usb_output_supported && type == DeviceType::USBOutput) {
- continue;
+ std::string out{};
+ for (u32 i = 0; i < out_count; i++) {
+ std::string a{};
+ u32 j = 0;
+ while (out_names[i].name[j] != '\0') {
+ a += out_names[i].name[j];
+ j++;
}
-
- const auto& device_name = audio_device_names[i];
- auto& entry = name_buffer.emplace_back();
- device_name.copy(entry.data(), device_name.size());
+ out += "\n\t" + a;
}
- ctx.WriteBuffer(name_buffer);
+ LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
IPC::ResponseBuilder rb{ctx, 3};
+
+ ctx.WriteBuffer(out_names);
+
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(name_buffer.size()));
+ rb.Push(out_count);
}
void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
@@ -243,7 +282,11 @@ private:
const auto device_name_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(device_name_buffer);
- LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume);
+ LOG_DEBUG(Service_Audio, "called. name={}, volume={}", name, volume);
+
+ if (name == "AudioTvOutput") {
+ impl->SetDeviceVolumes(volume);
+ }
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -253,53 +296,60 @@ private:
const auto device_name_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(device_name_buffer);
- LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name);
+ LOG_DEBUG(Service_Audio, "called. Name={}", name);
+
+ f32 volume{1.0f};
+ if (name == "AudioTvOutput") {
+ volume = impl->GetDeviceVolume(name);
+ }
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(1.0f);
+ rb.Push(volume);
}
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ const auto write_size = ctx.GetWriteBufferSize() / sizeof(char);
+ std::string out_name{"AudioTvOutput"};
- // Currently set to always be TV audio output.
- const auto& device_name = audio_device_names[2];
+ LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name);
- AudioDeviceName out_device_name{};
- device_name.copy(out_device_name.data(), device_name.size());
+ out_name.resize(write_size);
- ctx.WriteBuffer(out_device_name);
+ ctx.WriteBuffer(out_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "(STUBBED) called");
- buffer_event->GetWritableEvent().Signal();
+ event->GetWritableEvent().Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ const auto& sink{system.AudioCore().GetOutputSink()};
+ u32 channel_count{sink.GetDeviceChannels()};
+
+ LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count);
IPC::ResponseBuilder rb{ctx, 3};
+
rb.Push(ResultSuccess);
- rb.Push<u32>(2);
+ rb.Push<u32>(channel_count);
}
- // Should be similar to QueryAudioDeviceOutputEvent
void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) {
@@ -307,402 +357,167 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
- Kernel::KEvent* buffer_event;
- u32_le revision = 0;
+ void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) {
+ const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
+
+ std::vector<AudioDevice::AudioDeviceName> out_names{};
+
+ u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count);
+
+ std::string out{};
+ for (u32 i = 0; i < out_count; i++) {
+ std::string a{};
+ u32 j = 0;
+ while (out_names[i].name[j] != '\0') {
+ a += out_names[i].name[j];
+ j++;
+ }
+ out += "\n\t" + a;
+ }
+
+ LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ ctx.WriteBuffer(out_names);
+
+ rb.Push(ResultSuccess);
+ rb.Push(out_count);
+ }
+
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioDevice> impl;
+ Kernel::KEvent* event;
};
AudRenU::AudRenU(Core::System& system_)
- : ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"} {
+ : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew},
+ service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
- {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetWorkBufferSize"},
+ {1, &AudRenU::GetWorkBufferSize, "GetWorkBufferSize"},
{2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
- {3, &AudRenU::OpenAudioRendererForManualExecution, "OpenAudioRendererForManualExecution"},
+ {3, nullptr, "OpenAudioRendererForManualExecution"},
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
};
// clang-format on
RegisterHandlers(functions);
-
- buffer_event = service_context.CreateEvent("IAudioOutBufferReleasedEvent");
}
-AudRenU::~AudRenU() {
- service_context.CloseEvent(buffer_event);
-}
+AudRenU::~AudRenU() = default;
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- OpenAudioRendererImpl(ctx);
-}
-
-static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) {
- // +1 represents the final mix.
- return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
- 1;
-}
-
-void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- // Several calculations below align the sizes being calculated
- // onto a 64 byte boundary.
- static constexpr u64 buffer_alignment_size = 64;
-
- // Some calculations that calculate portions of the buffer
- // that will contain information, on the other hand, align
- // the result of some of their calcularions on a 16 byte boundary.
- static constexpr u64 info_field_alignment_size = 16;
-
- // Maximum detail entries that may exist at one time for performance
- // frame statistics.
- static constexpr u64 max_perf_detail_entries = 100;
-
- // Size of the data structure representing the bulk of the voice-related state.
- static constexpr u64 voice_state_size_bytes = 0x100;
-
- // Size of the upsampler manager data structure
- constexpr u64 upsampler_manager_size = 0x48;
-
- // Calculates the part of the size that relates to mix buffers.
- const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) {
- // As of 8.0.0 this is the maximum on voice channels.
- constexpr u64 max_voice_channels = 6;
-
- // The service expects the sample_count member of the parameters to either be
- // a value of 160 or 240, so the maximum sample count is assumed in order
- // to adequately handle all values at runtime.
- constexpr u64 default_max_sample_count = 240;
-
- const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels;
-
- u64 size = 0;
- size += total_mix_buffers * (sizeof(s32) * params.sample_count);
- size += total_mix_buffers * (sizeof(s32) * default_max_sample_count);
- size += u64{params.submix_count} + params.sink_count;
- size = Common::AlignUp(size, buffer_alignment_size);
- size += Common::AlignUp(params.unknown_30, buffer_alignment_size);
- size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size);
- return size;
- };
-
- // Calculates the portion of the size related to the mix data (and the sorting thereof).
- const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- // The size of the mixing info data structure.
- constexpr u64 mix_info_size = 0x940;
-
- // Consists of total submixes with the final mix included.
- const u64 total_mix_count = u64{params.submix_count} + 1;
-
- // The total number of effects that may be available to the audio renderer at any time.
- constexpr u64 max_effects = 256;
-
- // Calculates the part of the size related to the audio node state.
- // This will only be used if the audio revision supports the splitter.
- const auto calculate_node_state_size = [](std::size_t num_nodes) {
- // Internally within a nodestate, it appears to use a data structure
- // similar to a std::bitset<64> twice.
- constexpr u64 bit_size = Common::BitSize<u64>();
- constexpr u64 num_bitsets = 2;
-
- // Node state instances have three states internally for performing
- // depth-first searches of nodes. Initialized, Found, and Done Sorting.
- constexpr u64 num_states = 3;
-
- u64 size = 0;
- size += (num_nodes * num_nodes) * sizeof(s32);
- size += num_states * (num_nodes * sizeof(s32));
- size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>());
- return size;
- };
-
- // Calculates the part of the size related to the adjacency (aka edge) matrix.
- const auto calculate_edge_matrix_size = [](std::size_t num_nodes) {
- return (num_nodes * num_nodes) * sizeof(s32);
- };
-
- u64 size = 0;
- size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size);
- size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size);
- size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count,
- info_field_alignment_size);
-
- if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
- size += Common::AlignUp(calculate_node_state_size(total_mix_count) +
- calculate_edge_matrix_size(total_mix_count),
- info_field_alignment_size);
- }
-
- return size;
- };
-
- // Calculates the part of the size related to voice channel info.
- const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 voice_info_size = 0x220;
- constexpr u64 voice_resource_size = 0xD0;
-
- u64 size = 0;
- size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size);
- size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size);
- size +=
- Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size);
- size +=
- Common::AlignUp(voice_state_size_bytes * params.voice_count, info_field_alignment_size);
- return size;
- };
-
- // Calculates the part of the size related to memory pools.
- const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
- const u64 memory_pool_info_size = 0x20;
- return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to the splitter context.
- const auto calculate_splitter_context_size =
- [](const AudioCommon::AudioRendererParameter& params) -> u64 {
- if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
- return 0;
- }
-
- constexpr u64 splitter_info_size = 0x20;
- constexpr u64 splitter_destination_data_size = 0xE0;
-
- u64 size = 0;
- size += params.num_splitter_send_channels;
- size +=
- Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size);
- size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels,
- info_field_alignment_size);
-
- return size;
- };
-
- // Calculates the part of the size related to the upsampler info.
- const auto calculate_upsampler_info_size =
- [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 upsampler_info_size = 0x280;
- // Yes, using the buffer size over info alignment size is intentional here.
- return Common::AlignUp(upsampler_info_size *
- (u64{params.submix_count} + params.sink_count),
- buffer_alignment_size);
- };
-
- // Calculates the part of the size related to effect info.
- const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 effect_info_size = 0x2B0;
- return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to audio sink info.
- const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 sink_info_size = 0x170;
- return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to voice state info.
- const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 voice_state_size = 0x100;
- const u64 additional_size = buffer_alignment_size - 1;
- return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
- info_field_alignment_size);
- };
-
- // Calculates the part of the size related to performance statistics.
- const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) {
- // Extra size value appended to the end of the calculation.
- constexpr u64 appended = 128;
-
- // Whether or not we assume the newer version of performance metrics data structures.
- const bool is_v2 =
- IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision);
-
- // Data structure sizes
- constexpr u64 perf_statistics_size = 0x0C;
- const u64 header_size = is_v2 ? 0x30 : 0x18;
- const u64 entry_size = is_v2 ? 0x18 : 0x10;
- const u64 detail_size = is_v2 ? 0x18 : 0x10;
-
- const u64 entry_count = CalculateNumPerformanceEntries(params);
- const u64 size_per_frame =
- header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries);
-
- u64 size = 0;
- size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1,
- buffer_alignment_size);
- size += Common::AlignUp(perf_statistics_size, buffer_alignment_size);
- size += appended;
- return size;
- };
-
- // Calculates the part of the size that relates to the audio command buffer.
- const auto calculate_command_buffer_size =
- [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
-
- if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
- constexpr u64 command_buffer_size = 0x18000;
-
- return command_buffer_size + alignment;
- }
-
- // When the variadic command buffer is supported, this means
- // the command generator for the audio renderer can issue commands
- // that are (as one would expect), variable in size. So what we need to do
- // is determine the maximum possible size for a few command data structures
- // then multiply them by the amount of present commands indicated by the given
- // respective audio parameters.
-
- constexpr u64 max_biquad_filters = 2;
- constexpr u64 max_mix_buffers = 24;
-
- constexpr u64 biquad_filter_command_size = 0x2C;
-
- constexpr u64 depop_mix_command_size = 0x24;
- constexpr u64 depop_setup_command_size = 0x50;
-
- constexpr u64 effect_command_max_size = 0x540;
-
- constexpr u64 mix_command_size = 0x1C;
- constexpr u64 mix_ramp_command_size = 0x24;
- constexpr u64 mix_ramp_grouped_command_size = 0x13C;
-
- constexpr u64 perf_command_size = 0x28;
-
- constexpr u64 sink_command_size = 0x130;
-
- constexpr u64 submix_command_max_size =
- depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
-
- constexpr u64 volume_command_size = 0x1C;
- constexpr u64 volume_ramp_command_size = 0x20;
-
- constexpr u64 voice_biquad_filter_command_size =
- biquad_filter_command_size * max_biquad_filters;
- constexpr u64 voice_data_command_size = 0x9C;
- const u64 voice_command_max_size =
- (params.splitter_count * depop_setup_command_size) +
- (voice_data_command_size + voice_biquad_filter_command_size +
- volume_ramp_command_size + mix_ramp_grouped_command_size);
+ IPC::RequestParser rp{ctx};
- // Now calculate the individual elements that comprise the size and add them together.
- const u64 effect_commands_size = params.effect_count * effect_command_max_size;
+ AudioCore::AudioRendererParameterInternal params;
+ rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
+ auto transfer_memory_handle = ctx.GetCopyHandle(0);
+ auto process_handle = ctx.GetCopyHandle(1);
+ auto transfer_memory_size = rp.Pop<u64>();
+ auto applet_resource_user_id = rp.Pop<u64>();
- const u64 final_mix_commands_size =
- depop_mix_command_size + volume_command_size * max_mix_buffers;
+ if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) {
+ LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
+ return;
+ }
- const u64 perf_commands_size =
- perf_command_size *
- (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
+ const auto& handle_table{system.CurrentProcess()->GetHandleTable()};
+ auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
+ auto transfer_memory{
+ process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
- const u64 sink_commands_size = params.sink_count * sink_command_size;
+ const auto session_id{impl->GetSessionId()};
+ if (session_id == -1) {
+ LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
+ return;
+ }
- const u64 splitter_commands_size =
- params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
+ LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id,
+ impl->GetSessionCount());
- const u64 submix_commands_size = params.submix_count * submix_command_max_size;
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(),
+ transfer_memory_size, process_handle,
+ applet_resource_user_id, session_id);
+}
- const u64 voice_commands_size = params.voice_count * voice_command_max_size;
-
- return effect_commands_size + final_mix_commands_size + perf_commands_size +
- sink_commands_size + splitter_commands_size + submix_commands_size +
- voice_commands_size + alignment;
- };
+void AudRenU::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
+ AudioCore::AudioRendererParameterInternal params;
IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
-
- u64 size = 0;
- size += calculate_mix_buffer_sizes(params);
- size += calculate_mix_info_size(params);
- size += calculate_voice_info_size(params);
- size += upsampler_manager_size;
- size += calculate_memory_pools_size(params);
- size += calculate_splitter_context_size(params);
-
- size = Common::AlignUp(size, buffer_alignment_size);
-
- size += calculate_upsampler_info_size(params);
- size += calculate_effect_info_size(params);
- size += calculate_sink_info_size(params);
- size += calculate_voice_state_size(params);
- size += calculate_perf_size(params);
- size += calculate_command_buffer_size(params);
-
- // finally, 4KB page align the size, and we're done.
- size = Common::AlignUp(size, 4096);
+ rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
+
+ u64 size{0};
+ auto result = impl->GetWorkBufferSize(params, size);
+
+ std::string output_info{};
+ output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision));
+ output_info +=
+ fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count);
+ output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}",
+ static_cast<u32>(params.execution_mode), params.voice_drop_enabled);
+ output_info += fmt::format(
+ "\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos "
+ "{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External "
+ "Context {:04X}",
+ params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos,
+ params.splitter_destinations, params.voices, params.perf_frames,
+ params.external_context_size);
+
+ LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}",
+ output_info, size);
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
+ rb.Push(result);
rb.Push<u64>(size);
-
- LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size);
}
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const u64 aruid = rp.Pop<u64>();
- LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid);
+ const auto applet_resource_user_id = rp.Pop<u64>();
+
+ LOG_DEBUG(Service_Audio, "called. Applet resource id {}", applet_resource_user_id);
- // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that
- // always assumes the initial release revision (REV1).
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioDevice>(system, buffer_event, Common::MakeMagic('R', 'E', 'V', '1'));
+ rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id,
+ ::Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++);
}
void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
-
- OpenAudioRendererImpl(ctx);
}
void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
struct Parameters {
u32 revision;
- u64 aruid;
+ u64 applet_resource_user_id;
};
IPC::RequestParser rp{ctx};
- const auto [revision, aruid] = rp.PopRaw<Parameters>();
- LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid);
+ const auto [revision, applet_resource_user_id] = rp.PopRaw<Parameters>();
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioDevice>(system, buffer_event, revision);
-}
+ LOG_DEBUG(Service_Audio, "called. Revision {} Applet resource id {}",
+ AudioCore::GetRevisionNum(revision), applet_resource_user_id);
-void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++);
-}
-
-bool IsFeatureSupported(AudioFeatures feature, u32_le revision) {
- // Byte swap
- const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
-
- switch (feature) {
- case AudioFeatures::AudioUSBDeviceOutput:
- return version_num >= 4U;
- case AudioFeatures::Splitter:
- return version_num >= 2U;
- case AudioFeatures::PerformanceMetricsVersion2:
- case AudioFeatures::VariadicCommandBuffer:
- return version_num >= 5U;
- default:
- return false;
- }
+ rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, revision,
+ num_audio_devices++);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index 869d39002..4384a9b3c 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -3,6 +3,7 @@
#pragma once
+#include "audio_core/audio_render_manager.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
@@ -15,6 +16,7 @@ class HLERequestContext;
}
namespace Service::Audio {
+class IAudioRenderer;
class AudRenU final : public ServiceFramework<AudRenU> {
public:
@@ -23,28 +25,14 @@ public:
private:
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
- void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
+ void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
void GetAudioDeviceService(Kernel::HLERequestContext& ctx);
void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx);
void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx);
- void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx);
-
KernelHelpers::ServiceContext service_context;
-
- std::size_t audren_instance_count = 0;
- Kernel::KEvent* buffer_event;
+ std::unique_ptr<AudioCore::AudioRenderer::Manager> impl;
+ u32 num_audio_devices{0};
};
-// Describes a particular audio feature that may be supported in a particular revision.
-enum class AudioFeatures : u32 {
- AudioUSBDeviceOutput,
- Splitter,
- PerformanceMetricsVersion2,
- VariadicCommandBuffer,
-};
-
-// Tests if a particular audio feature is supported with a given audio revision.
-bool IsFeatureSupported(AudioFeatures feature, u32_le revision);
-
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h
index ac6c514af..d706978cb 100644
--- a/src/core/hle/service/audio/errors.h
+++ b/src/core/hle/service/audio/errors.h
@@ -7,8 +7,17 @@
namespace Service::Audio {
+constexpr Result ERR_INVALID_DEVICE_NAME{ErrorModule::Audio, 1};
constexpr Result ERR_OPERATION_FAILED{ErrorModule::Audio, 2};
+constexpr Result ERR_INVALID_SAMPLE_RATE{ErrorModule::Audio, 3};
+constexpr Result ERR_INSUFFICIENT_BUFFER_SIZE{ErrorModule::Audio, 4};
+constexpr Result ERR_MAXIMUM_SESSIONS_REACHED{ErrorModule::Audio, 5};
constexpr Result ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8};
+constexpr Result ERR_INVALID_CHANNEL_COUNT{ErrorModule::Audio, 10};
+constexpr Result ERR_INVALID_UPDATE_DATA{ErrorModule::Audio, 41};
+constexpr Result ERR_POOL_MAPPING_FAILED{ErrorModule::Audio, 42};
constexpr Result ERR_NOT_SUPPORTED{ErrorModule::Audio, 513};
+constexpr Result ERR_INVALID_PROCESS_HANDLE{ErrorModule::Audio, 1536};
+constexpr Result ERR_INVALID_REVISION{ErrorModule::Audio, 1537};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 75da659e5..4f2ed2d52 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -298,7 +298,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) {
const auto sample_rate = rp.Pop<u32>();
const auto channel_count = rp.Pop<u32>();
- LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
+ LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
sample_rate == 12000 || sample_rate == 8000,
diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp
index d928e37fb..bc08ac487 100644
--- a/src/core/hle/service/bcat/bcat_module.cpp
+++ b/src/core/hle/service/bcat/bcat_module.cpp
@@ -140,8 +140,8 @@ public:
{20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
{20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
{30100, &IBcatService::SetPassphrase, "SetPassphrase"},
- {30101, nullptr, "Unknown"},
- {30102, nullptr, "Unknown2"},
+ {30101, nullptr, "Unknown30101"},
+ {30102, nullptr, "Unknown30102"},
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index f9ee2b624..ec7e5320c 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -181,6 +181,11 @@ public:
{147, nullptr, "RegisterAudioControlNotification"},
{148, nullptr, "SendAudioControlPassthroughCommand"},
{149, nullptr, "SendAudioControlSetAbsoluteVolumeCommand"},
+ {150, nullptr, "AcquireAudioSinkVolumeLocallyChangedEvent"},
+ {151, nullptr, "AcquireAudioSinkVolumeUpdateRequestCompletedEvent"},
+ {152, nullptr, "GetAudioSinkVolume"},
+ {153, nullptr, "RequestUpdateAudioSinkVolume"},
+ {154, nullptr, "IsAudioSinkVolumeSupported"},
{256, nullptr, "IsManufacturingMode"},
{257, nullptr, "EmulateBluetoothCrash"},
{258, nullptr, "GetBleChannelMap"},
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 3fa88cbd3..eebf85e03 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -214,8 +214,12 @@ public:
{76, nullptr, "Unknown76"},
{100, nullptr, "Unknown100"},
{101, nullptr, "Unknown101"},
- {110, nullptr, "Unknown102"},
- {111, nullptr, "Unknown103"},
+ {110, nullptr, "Unknown110"},
+ {111, nullptr, "Unknown111"},
+ {112, nullptr, "Unknown112"},
+ {113, nullptr, "Unknown113"},
+ {114, nullptr, "Unknown114"},
+ {115, nullptr, "Unknown115"},
};
// clang-format on
diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp
index 7d35b4208..4a81bb5e2 100644
--- a/src/core/hle/service/fatal/fatal_p.cpp
+++ b/src/core/hle/service/fatal/fatal_p.cpp
@@ -6,7 +6,13 @@
namespace Service::Fatal {
Fatal_P::Fatal_P(std::shared_ptr<Module> module_, Core::System& system_)
- : Interface(std::move(module_), system_, "fatal:p") {}
+ : Interface(std::move(module_), system_, "fatal:p") {
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetFatalEvent"},
+ {10, nullptr, "GetFatalContext"},
+ };
+ RegisterHandlers(functions);
+}
Fatal_P::~Fatal_P() = default;
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 aa7189bda..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);
@@ -838,11 +885,11 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
const auto now = steady_clock::now();
- // Filter out non-zero vibrations that are within 10ms of each other.
+ // Filter out non-zero vibrations that are within 15ms of each other.
if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
duration_cast<milliseconds>(
now - controller.vibration[device_index].last_vibration_timepoint) <
- milliseconds(10)) {
+ milliseconds(15)) {
return false;
}
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 46282f42e..4613a4e60 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -19,3 +19,10 @@ constexpr Result InvalidNpadId{ErrorModule::HID, 709};
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
} // namespace Service::HID
+
+namespace Service::IRS {
+
+constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
+constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 89bb12442..7909141c0 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -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"},
@@ -2345,8 +2351,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
std::make_shared<HidSys>(system)->InstallAsService(service_manager);
std::make_shared<HidTmp>(system)->InstallAsService(service_manager);
- std::make_shared<IRS>(system)->InstallAsService(service_manager);
- std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager);
+ std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager);
+ std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager);
std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager);
}
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index d2a91d913..c4b44cbf9 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -1,16 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <algorithm>
+#include <random>
+
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/irs.h"
+#include "core/hle/service/hid/irsensor/clustering_processor.h"
+#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
+#include "core/hle/service/hid/irsensor/ir_led_processor.h"
+#include "core/hle/service/hid/irsensor/moment_processor.h"
+#include "core/hle/service/hid/irsensor/pointing_processor.h"
+#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
+#include "core/memory.h"
-namespace Service::HID {
+namespace Service::IRS {
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
// clang-format off
@@ -36,14 +48,19 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
};
// clang-format on
+ u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer();
RegisterHandlers(functions);
+ shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory));
+
+ npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
}
+IRS::~IRS() = default;
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -54,7 +71,7 @@ void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -75,7 +92,7 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -88,17 +105,23 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Stop Image processor
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedMomentProcessorConfig processor_config;
+ Core::IrSensor::PackedMomentProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
@@ -109,19 +132,28 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedClusteringProcessorConfig processor_config;
+ Core::IrSensor::PackedClusteringProcessorConfig processor_config;
};
- static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size.");
+ static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -130,17 +162,27 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ClusteringProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedImageTransferProcessorConfig processor_config;
+ Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
u32 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
@@ -151,20 +193,42 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
auto t_mem =
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
- "applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.transfer_memory_size, parameters.applet_resource_user_id);
+ if (t_mem.IsNull()) {
+ LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
+
+ u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
+
+ LOG_INFO(Service_IRS,
+ "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -172,32 +236,68 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.applet_resource_user_id);
+ LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+
+ if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(InvalidProcessorState);
+ return;
+ }
- IPC::ResponseBuilder rb{ctx, 5};
+ std::vector<u8> data{};
+ const auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ const auto& state = image_transfer_processor.GetState(data);
+
+ ctx.WriteBuffer(data);
+ IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
- rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks());
- rb.PushRaw<u32>(0);
+ rb.PushRaw(state);
}
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ Core::IrSensor::PackedTeraPluginProcessorConfig processor_config;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
- "applet_resource_user_id={}",
- camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
- processor_config.required_mcu_version.major,
- processor_config.required_mcu_version.minor, applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major,
+ parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
@@ -207,17 +307,17 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
npad_id != Core::HID::NpadIdType::Handheld) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(InvalidNpadId);
+ rb.Push(Service::HID::InvalidNpadId);
return;
}
- IrCameraHandle camera_handle{
+ Core::IrSensor::IrCameraHandle camera_handle{
.npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
.npad_type = Core::HID::NpadStyleIndex::None,
};
- LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}",
- npad_id, camera_handle.npad_id, camera_handle.npad_type);
+ LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
+ camera_handle.npad_id, camera_handle.npad_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -226,8 +326,8 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(
@@ -236,14 +336,23 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
+ MakeProcessor<PointingProcessor>(camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
+ image_transfer_processor.SetConfig(processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -256,14 +365,20 @@ void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Suspend image processor
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto mcu_version{rp.PopRaw<PackedMcuVersion>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(
@@ -272,37 +387,45 @@ void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
mcu_version.minor);
+ auto result = IsIrCameraHandleValid(camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Check firmware version
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- struct Parameters {
- IrCameraHandle camera_handle;
- PackedFunctionLevel function_level;
- u64 applet_resource_user_id;
- };
- static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
-
- const auto parameters{rp.PopRaw<Parameters>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.applet_resource_user_id);
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
+ camera_handle.npad_type, camera_handle.npad_id, function_level.function_level,
+ applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Set Function level
+ result = ResultSuccess;
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedImageTransferProcessorExConfig processor_config;
+ Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
u64 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
@@ -313,20 +436,33 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
auto t_mem =
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
- "applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.transfer_memory_size, parameters.applet_resource_user_id);
+ u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
+
+ LOG_INFO(Service_IRS,
+ "called, npad_type={}, npad_id={}, transfer_memory_size={}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.transfer_memory_size, parameters.applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS,
@@ -336,14 +472,23 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
+ MakeProcessor<IrLedProcessor>(camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
+ image_transfer_processor.SetConfig(processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -356,14 +501,20 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Stop image processor async
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- PackedFunctionLevel function_level;
+ Core::IrSensor::PackedFunctionLevel function_level;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -378,7 +529,22 @@ void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
-IRS::~IRS() = default;
+Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
+ if (camera_handle.npad_id >
+ static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
+ return InvalidIrCameraHandle;
+ }
+ if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
+ return InvalidIrCameraHandle;
+ }
+ return ResultSuccess;
+}
+
+Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry(
+ const Core::IrSensor::IrCameraHandle& camera_handle) {
+ ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id");
+ return shared_memory->device[camera_handle.npad_id];
+}
IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
// clang-format off
@@ -395,4 +561,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
IRS_SYS::~IRS_SYS() = default;
-} // namespace Service::HID
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index 361dc2213..2e6115c73 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -4,13 +4,19 @@
#pragma once
#include "core/hid/hid_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
-namespace Service::HID {
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::IRS {
class IRS final : public ServiceFramework<IRS> {
public:
@@ -18,234 +24,19 @@ public:
~IRS() override;
private:
- // This is nn::irsensor::IrCameraStatus
- enum IrCameraStatus : u32 {
- Available,
- Unsupported,
- Unconnected,
- };
-
- // This is nn::irsensor::IrCameraInternalStatus
- enum IrCameraInternalStatus : u32 {
- Stopped,
- FirmwareUpdateNeeded,
- Unkown2,
- Unkown3,
- Unkown4,
- FirmwareVersionRequested,
- FirmwareVersionIsInvalid,
- Ready,
- Setting,
- };
-
- // This is nn::irsensor::detail::StatusManager::IrSensorMode
- enum IrSensorMode : u64 {
- None,
- MomentProcessor,
- ClusteringProcessor,
- ImageTransferProcessor,
- PointingProcessorMarker,
- TeraPluginProcessor,
- IrLedProcessor,
- };
-
- // This is nn::irsensor::ImageProcessorStatus
- enum ImageProcessorStatus : u8 {
- stopped,
- running,
- };
-
- // This is nn::irsensor::ImageTransferProcessorFormat
- enum ImageTransferProcessorFormat : u8 {
- Size320x240,
- Size160x120,
- Size80x60,
- Size40x30,
- Size20x15,
- };
-
- // This is nn::irsensor::AdaptiveClusteringMode
- enum AdaptiveClusteringMode : u8 {
- StaticFov,
- DynamicFov,
- };
-
- // This is nn::irsensor::AdaptiveClusteringTargetDistance
- enum AdaptiveClusteringTargetDistance : u8 {
- Near,
- Middle,
- Far,
- };
-
- // This is nn::irsensor::IrsHandAnalysisMode
- enum IrsHandAnalysisMode : u8 {
- Silhouette,
- Image,
- SilhoueteAndImage,
- SilhuetteOnly,
- };
-
- // This is nn::irsensor::IrSensorFunctionLevel
- enum IrSensorFunctionLevel : u8 {
- unknown0,
- unknown1,
- unknown2,
- unknown3,
- unknown4,
- };
-
- // This is nn::irsensor::IrCameraHandle
- struct IrCameraHandle {
- u8 npad_id{};
- Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
- INSERT_PADDING_BYTES(2);
- };
- static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
-
- struct IrsRect {
- s16 x;
- s16 y;
- s16 width;
- s16 height;
+ // This is nn::irsensor::detail::AruidFormat
+ struct AruidFormat {
+ u64 sensor_aruid;
+ u64 sensor_aruid_status;
};
+ static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size");
- // This is nn::irsensor::PackedMcuVersion
- struct PackedMcuVersion {
- u16 major;
- u16 minor;
+ // This is nn::irsensor::detail::StatusManager
+ struct StatusManager {
+ std::array<Core::IrSensor::DeviceFormat, 9> device;
+ std::array<AruidFormat, 5> aruid;
};
- static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
-
- // This is nn::irsensor::MomentProcessorConfig
- struct MomentProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(7);
- IrsRect window_of_interest;
- u8 preprocess;
- u8 preprocess_intensity_threshold;
- INSERT_PADDING_BYTES(5);
- };
- static_assert(sizeof(MomentProcessorConfig) == 0x28,
- "MomentProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedMomentProcessorConfig
- struct PackedMomentProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- IrsRect window_of_interest;
- PackedMcuVersion required_mcu_version;
- u8 preprocess;
- u8 preprocess_intensity_threshold;
- INSERT_PADDING_BYTES(2);
- };
- static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
- "PackedMomentProcessorConfig is an invalid size");
-
- // This is nn::irsensor::ClusteringProcessorConfig
- struct ClusteringProcessorConfig {
- u64 exposire_time;
- u32 light_target;
- u32 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(7);
- IrsRect window_of_interest;
- u32 pixel_count_min;
- u32 pixel_count_max;
- u32 object_intensity_min;
- u8 is_external_light_filter_enabled;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
- "ClusteringProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedClusteringProcessorConfig
- struct PackedClusteringProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- IrsRect window_of_interest;
- PackedMcuVersion required_mcu_version;
- u32 pixel_count_min;
- u32 pixel_count_max;
- u32 object_intensity_min;
- u8 is_external_light_filter_enabled;
- INSERT_PADDING_BYTES(2);
- };
- static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
- "PackedClusteringProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedImageTransferProcessorConfig
- struct PackedImageTransferProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- PackedMcuVersion required_mcu_version;
- u8 format;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
- "PackedImageTransferProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedTeraPluginProcessorConfig
- struct PackedTeraPluginProcessorConfig {
- PackedMcuVersion required_mcu_version;
- u8 mode;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
- "PackedTeraPluginProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedPointingProcessorConfig
- struct PackedPointingProcessorConfig {
- IrsRect window_of_interest;
- PackedMcuVersion required_mcu_version;
- };
- static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
- "PackedPointingProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedFunctionLevel
- struct PackedFunctionLevel {
- IrSensorFunctionLevel function_level;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
-
- // This is nn::irsensor::PackedImageTransferProcessorExConfig
- struct PackedImageTransferProcessorExConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- PackedMcuVersion required_mcu_version;
- ImageTransferProcessorFormat origin_format;
- ImageTransferProcessorFormat trimming_format;
- u16 trimming_start_x;
- u16 trimming_start_y;
- u8 is_external_light_filter_enabled;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
- "PackedImageTransferProcessorExConfig is an invalid size");
-
- // This is nn::irsensor::PackedIrLedProcessorConfig
- struct PackedIrLedProcessorConfig {
- PackedMcuVersion required_mcu_version;
- u8 light_target;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
- "PackedIrLedProcessorConfig is an invalid size");
+ static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
@@ -265,6 +56,56 @@ private:
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
+
+ Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
+ Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
+ const Core::IrSensor::IrCameraHandle& camera_handle);
+
+ template <typename T>
+ void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle,
+ Core::IrSensor::DeviceFormat& device_state) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return;
+ }
+ processors[index] = std::make_unique<T>(device_state);
+ }
+
+ template <typename T>
+ void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle,
+ Core::IrSensor::DeviceFormat& device_state) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return;
+ }
+ processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index);
+ }
+
+ template <typename T>
+ T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return static_cast<T&>(*processors[0]);
+ }
+ return static_cast<T&>(*processors[index]);
+ }
+
+ template <typename T>
+ const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return static_cast<T&>(*processors[0]);
+ }
+ return static_cast<T&>(*processors[index]);
+ }
+
+ Core::HID::EmulatedController* npad_device = nullptr;
+ StatusManager* shared_memory = nullptr;
+ std::array<std::unique_ptr<ProcessorBase>, 9> processors{};
};
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
@@ -273,4 +114,4 @@ public:
~IRS_SYS() override;
};
-} // namespace Service::HID
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irs_ring_lifo.h b/src/core/hle/service/hid/irs_ring_lifo.h
new file mode 100644
index 000000000..255d1d296
--- /dev/null
+++ b/src/core/hle/service/hid/irs_ring_lifo.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+
+namespace Service::IRS {
+
+template <typename State, std::size_t max_buffer_size>
+struct Lifo {
+ s64 sampling_number{};
+ s64 buffer_count{};
+ std::array<State, max_buffer_size> entries{};
+
+ const State& ReadCurrentEntry() const {
+ return entries[GetBufferTail()];
+ }
+
+ const State& ReadPreviousEntry() const {
+ return entries[GetPreviousEntryIndex()];
+ }
+
+ s64 GetBufferTail() const {
+ return sampling_number % max_buffer_size;
+ }
+
+ std::size_t GetPreviousEntryIndex() const {
+ return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size);
+ }
+
+ std::size_t GetNextEntryIndex() const {
+ return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size);
+ }
+
+ void WriteNextEntry(const State& new_state) {
+ if (buffer_count < static_cast<s64>(max_buffer_size)) {
+ buffer_count++;
+ }
+ sampling_number++;
+ entries[GetBufferTail()] = new_state;
+ }
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
new file mode 100644
index 000000000..e2f4ae876
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
@@ -0,0 +1,265 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <queue>
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/irsensor/clustering_processor.h"
+
+namespace Service::IRS {
+ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index)
+ : device{device_format} {
+ npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
+
+ device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+ SetDefaultConfig();
+
+ shared_memory = std::construct_at(
+ reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data));
+
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
+ .is_npad_service = true,
+ };
+ callback_key = npad_device->SetCallback(engine_callback);
+}
+
+ClusteringProcessor::~ClusteringProcessor() {
+ npad_device->DeleteCallback(callback_key);
+};
+
+void ClusteringProcessor::StartProcessor() {
+ device.camera_status = Core::IrSensor::IrCameraStatus::Available;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
+}
+
+void ClusteringProcessor::SuspendProcessor() {}
+
+void ClusteringProcessor::StopProcessor() {}
+
+void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
+ if (type != Core::HID::ControllerTriggerType::IrSensor) {
+ return;
+ }
+
+ next_state = {};
+ const auto camera_data = npad_device->GetCamera();
+ auto filtered_image = camera_data.data;
+
+ RemoveLowIntensityData(filtered_image);
+
+ const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
+ const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
+ const auto window_end_x =
+ window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width);
+ const auto window_end_y =
+ window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height);
+
+ for (std::size_t y = window_start_y; y < window_end_y; y++) {
+ for (std::size_t x = window_start_x; x < window_end_x; x++) {
+ u8 pixel = GetPixel(filtered_image, x, y);
+ if (pixel == 0) {
+ continue;
+ }
+ const auto cluster = GetClusterProperties(filtered_image, x, y);
+ if (cluster.pixel_count > current_config.pixel_count_max) {
+ continue;
+ }
+ if (cluster.pixel_count < current_config.pixel_count_min) {
+ continue;
+ }
+ // Cluster object limit reached
+ if (next_state.object_count >= next_state.data.size()) {
+ continue;
+ }
+ next_state.data[next_state.object_count] = cluster;
+ next_state.object_count++;
+ }
+ }
+
+ next_state.sampling_number = camera_data.sample;
+ next_state.timestamp = next_state.timestamp + 131;
+ next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+ shared_memory->clustering_lifo.WriteNextEntry(next_state);
+
+ if (!IsProcessorActive()) {
+ StartProcessor();
+ }
+}
+
+void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) {
+ for (u8& pixel : data) {
+ if (pixel < current_config.pixel_count_min) {
+ pixel = 0;
+ }
+ }
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data,
+ std::size_t x,
+ std::size_t y) {
+ using DataPoint = Common::Point<std::size_t>;
+ std::queue<DataPoint> search_points{};
+ ClusteringData current_cluster = GetPixelProperties(data, x, y);
+ SetPixel(data, x, y, 0);
+ search_points.emplace<DataPoint>({x, y});
+
+ while (!search_points.empty()) {
+ const auto point = search_points.front();
+ search_points.pop();
+
+ // Avoid negative numbers
+ if (point.x == 0 || point.y == 0) {
+ continue;
+ }
+
+ std::array<DataPoint, 4> new_points{
+ DataPoint{point.x - 1, point.y},
+ {point.x, point.y - 1},
+ {point.x + 1, point.y},
+ {point.x, point.y + 1},
+ };
+
+ for (const auto new_point : new_points) {
+ if (new_point.x >= width) {
+ continue;
+ }
+ if (new_point.y >= height) {
+ continue;
+ }
+ if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) {
+ continue;
+ }
+ const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y);
+ current_cluster = MergeCluster(current_cluster, cluster);
+ SetPixel(data, new_point.x, new_point.y, 0);
+ search_points.emplace<DataPoint>({new_point.x, new_point.y});
+ }
+ }
+
+ return current_cluster;
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties(
+ const std::vector<u8>& data, std::size_t x, std::size_t y) const {
+ return {
+ .average_intensity = GetPixel(data, x, y) / 255.0f,
+ .centroid =
+ {
+ .x = static_cast<f32>(x),
+ .y = static_cast<f32>(y),
+
+ },
+ .pixel_count = 1,
+ .bound =
+ {
+ .x = static_cast<s16>(x),
+ .y = static_cast<s16>(y),
+ .width = 1,
+ .height = 1,
+ },
+ };
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
+ const ClusteringData a, const ClusteringData b) const {
+ const f32 a_pixel_count = static_cast<f32>(a.pixel_count);
+ const f32 b_pixel_count = static_cast<f32>(b.pixel_count);
+ const f32 pixel_count = a_pixel_count + b_pixel_count;
+ const f32 average_intensity =
+ (a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count;
+ const Core::IrSensor::IrsCentroid centroid = {
+ .x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count,
+ .y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count,
+ };
+ s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x;
+ s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y;
+ s16 a_bound_end_x = a.bound.x + a.bound.width;
+ s16 a_bound_end_y = a.bound.y + a.bound.height;
+ s16 b_bound_end_x = b.bound.x + b.bound.width;
+ s16 b_bound_end_y = b.bound.y + b.bound.height;
+
+ const Core::IrSensor::IrsRect bound = {
+ .x = bound_start_x,
+ .y = bound_start_y,
+ .width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x)
+ : static_cast<s16>(b_bound_end_x - bound_start_x),
+ .height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y)
+ : static_cast<s16>(b_bound_end_y - bound_start_y),
+ };
+
+ return {
+ .average_intensity = average_intensity,
+ .centroid = centroid,
+ .pixel_count = static_cast<u32>(pixel_count),
+ .bound = bound,
+ };
+}
+
+u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
+ if ((y * width) + x > data.size()) {
+ return 0;
+ }
+ return data[(y * width) + x];
+}
+
+void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
+ if ((y * width) + x > data.size()) {
+ return;
+ }
+ data[(y * width) + x] = value;
+}
+
+void ClusteringProcessor::SetDefaultConfig() {
+ using namespace std::literals::chrono_literals;
+ current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count();
+ current_config.camera_config.gain = 2;
+ current_config.camera_config.is_negative_used = false;
+ current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds;
+ current_config.window_of_interest = {
+ .x = 0,
+ .y = 0,
+ .width = width,
+ .height = height,
+ };
+ current_config.pixel_count_min = 3;
+ current_config.pixel_count_max = static_cast<u32>(GetDataSize(format));
+ current_config.is_external_light_filter_enabled = true;
+ current_config.object_intensity_min = 150;
+
+ npad_device->SetCameraFormat(format);
+}
+
+void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.window_of_interest = config.window_of_interest;
+ current_config.pixel_count_min = config.pixel_count_min;
+ current_config.pixel_count_max = config.pixel_count_max;
+ current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
+ current_config.object_intensity_min = config.object_intensity_min;
+
+ LOG_INFO(Service_IRS,
+ "Processor config, exposure_time={}, gain={}, is_negative_used={}, "
+ "light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, "
+ "pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}",
+ current_config.camera_config.exposure_time, current_config.camera_config.gain,
+ current_config.camera_config.is_negative_used,
+ current_config.camera_config.light_target, current_config.window_of_interest.x,
+ current_config.window_of_interest.y, current_config.window_of_interest.width,
+ current_config.window_of_interest.height, current_config.pixel_count_min,
+ current_config.pixel_count_max, current_config.is_external_light_filter_enabled,
+ current_config.object_intensity_min);
+
+ npad_device->SetCameraFormat(format);
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h
new file mode 100644
index 000000000..dc01a8ea7
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.h
@@ -0,0 +1,110 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irs_ring_lifo.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::IRS {
+class ClusteringProcessor final : public ProcessorBase {
+public:
+ explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index);
+ ~ClusteringProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
+
+private:
+ static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240;
+ static constexpr std::size_t width = 320;
+ static constexpr std::size_t height = 240;
+
+ // This is nn::irsensor::ClusteringProcessorConfig
+ struct ClusteringProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::IrsRect window_of_interest;
+ u32 pixel_count_min;
+ u32 pixel_count_max;
+ u32 object_intensity_min;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
+ "ClusteringProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::AdaptiveClusteringProcessorConfig
+ struct AdaptiveClusteringProcessorConfig {
+ Core::IrSensor::AdaptiveClusteringMode mode;
+ Core::IrSensor::AdaptiveClusteringTargetDistance target_distance;
+ };
+ static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8,
+ "AdaptiveClusteringProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::ClusteringData
+ struct ClusteringData {
+ f32 average_intensity;
+ Core::IrSensor::IrsCentroid centroid;
+ u32 pixel_count;
+ Core::IrSensor::IrsRect bound;
+ };
+ static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size");
+
+ // This is nn::irsensor::ClusteringProcessorState
+ struct ClusteringProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ u8 object_count;
+ INSERT_PADDING_BYTES(3);
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ std::array<ClusteringData, 0x10> data;
+ };
+ static_assert(sizeof(ClusteringProcessorState) == 0x198,
+ "ClusteringProcessorState is an invalid size");
+
+ struct ClusteringSharedMemory {
+ Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo;
+ static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size");
+ INSERT_PADDING_WORDS(0x11F);
+ };
+ static_assert(sizeof(ClusteringSharedMemory) == 0xE20,
+ "ClusteringSharedMemory is an invalid size");
+
+ void OnControllerUpdate(Core::HID::ControllerTriggerType type);
+ void RemoveLowIntensityData(std::vector<u8>& data);
+ ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y);
+ ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x,
+ std::size_t y) const;
+ ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const;
+ u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
+ void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value);
+
+ // Sets config parameters of the camera
+ void SetDefaultConfig();
+
+ ClusteringSharedMemory* shared_memory = nullptr;
+ ClusteringProcessorState next_state{};
+
+ ClusteringProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+ Core::HID::EmulatedController* npad_device;
+ int callback_key{};
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
new file mode 100644
index 000000000..98f0c579d
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
@@ -0,0 +1,150 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
+
+namespace Service::IRS {
+ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index)
+ : device{device_format} {
+ npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
+
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
+ .is_npad_service = true,
+ };
+ callback_key = npad_device->SetCallback(engine_callback);
+
+ device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+ImageTransferProcessor::~ImageTransferProcessor() {
+ npad_device->DeleteCallback(callback_key);
+};
+
+void ImageTransferProcessor::StartProcessor() {
+ is_active = true;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Available;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
+ processor_state.sampling_number = 0;
+ processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+}
+
+void ImageTransferProcessor::SuspendProcessor() {}
+
+void ImageTransferProcessor::StopProcessor() {}
+
+void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
+ if (type != Core::HID::ControllerTriggerType::IrSensor) {
+ return;
+ }
+ if (!is_transfer_memory_set) {
+ return;
+ }
+
+ const auto camera_data = npad_device->GetCamera();
+
+ // This indicates how much ambient light is precent
+ processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+ processor_state.sampling_number = camera_data.sample;
+
+ if (camera_data.format != current_config.origin_format) {
+ LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
+ current_config.origin_format);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ if (current_config.origin_format > current_config.trimming_format) {
+ LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
+ current_config.origin_format, current_config.trimming_format);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ std::vector<u8> window_data{};
+ const auto origin_width = GetDataWidth(current_config.origin_format);
+ const auto origin_height = GetDataHeight(current_config.origin_format);
+ const auto trimming_width = GetDataWidth(current_config.trimming_format);
+ const auto trimming_height = GetDataHeight(current_config.trimming_format);
+ window_data.resize(GetDataSize(current_config.trimming_format));
+
+ if (trimming_width + current_config.trimming_start_x > origin_width ||
+ trimming_height + current_config.trimming_start_y > origin_height) {
+ LOG_WARNING(Service_IRS,
+ "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
+ current_config.trimming_start_x, current_config.trimming_start_y,
+ trimming_width, trimming_height, origin_width, origin_height);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ for (std::size_t y = 0; y < trimming_height; y++) {
+ for (std::size_t x = 0; x < trimming_width; x++) {
+ const std::size_t window_index = (y * trimming_width) + x;
+ const std::size_t origin_index =
+ ((y + current_config.trimming_start_y) * origin_width) + x +
+ current_config.trimming_start_x;
+ window_data[window_index] = camera_data.data[origin_index];
+ }
+ }
+
+ memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format));
+
+ if (!IsProcessorActive()) {
+ StartProcessor();
+ }
+}
+
+void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.origin_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
+ current_config.trimming_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
+ current_config.trimming_start_x = 0;
+ current_config.trimming_start_y = 0;
+
+ npad_device->SetCameraFormat(current_config.origin_format);
+}
+
+void ImageTransferProcessor::SetConfig(
+ Core::IrSensor::PackedImageTransferProcessorExConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.origin_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format);
+ current_config.trimming_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format);
+ current_config.trimming_start_x = config.trimming_start_x;
+ current_config.trimming_start_y = config.trimming_start_y;
+
+ npad_device->SetCameraFormat(current_config.origin_format);
+}
+
+void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) {
+ is_transfer_memory_set = true;
+ transfer_memory = t_mem;
+}
+
+Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
+ std::vector<u8>& data) const {
+ const auto size = GetDataSize(current_config.trimming_format);
+ data.resize(size);
+ memcpy(data.data(), transfer_memory, size);
+ return processor_state;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.h b/src/core/hle/service/hid/irsensor/image_transfer_processor.h
new file mode 100644
index 000000000..393df492d
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.h
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::IRS {
+class ImageTransferProcessor final : public ProcessorBase {
+public:
+ explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index);
+ ~ImageTransferProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config);
+ void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
+
+ // Transfer memory where the image data will be stored
+ void SetTransferMemoryPointer(u8* t_mem);
+
+ Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
+
+private:
+ // This is nn::irsensor::ImageTransferProcessorConfig
+ struct ImageTransferProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::ImageTransferProcessorFormat format;
+ };
+ static_assert(sizeof(ImageTransferProcessorConfig) == 0x20,
+ "ImageTransferProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::ImageTransferProcessorExConfig
+ struct ImageTransferProcessorExConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::ImageTransferProcessorFormat origin_format;
+ Core::IrSensor::ImageTransferProcessorFormat trimming_format;
+ u16 trimming_start_x;
+ u16 trimming_start_y;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28,
+ "ImageTransferProcessorExConfig is an invalid size");
+
+ void OnControllerUpdate(Core::HID::ControllerTriggerType type);
+
+ ImageTransferProcessorExConfig current_config{};
+ Core::IrSensor::ImageTransferProcessorState processor_state{};
+ Core::IrSensor::DeviceFormat& device;
+ Core::HID::EmulatedController* npad_device;
+ int callback_key{};
+
+ u8* transfer_memory = nullptr;
+ bool is_transfer_memory_set = false;
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.cpp b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp
new file mode 100644
index 000000000..8e6dd99e4
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/ir_led_processor.h"
+
+namespace Service::IRS {
+IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+IrLedProcessor::~IrLedProcessor() = default;
+
+void IrLedProcessor::StartProcessor() {}
+
+void IrLedProcessor::SuspendProcessor() {}
+
+void IrLedProcessor::StopProcessor() {}
+
+void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) {
+ current_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.light_target);
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.h b/src/core/hle/service/hid/irsensor/ir_led_processor.h
new file mode 100644
index 000000000..c3d8693c9
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/ir_led_processor.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class IrLedProcessor final : public ProcessorBase {
+public:
+ explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~IrLedProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config);
+
+private:
+ // This is nn::irsensor::IrLedProcessorConfig
+ struct IrLedProcessorConfig {
+ Core::IrSensor::CameraLightTarget light_target;
+ };
+ static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size");
+
+ struct IrLedProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ std::array<u8, 0x8> data;
+ };
+ static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size");
+
+ IrLedProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp
new file mode 100644
index 000000000..dbaca420a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/moment_processor.h"
+
+namespace Service::IRS {
+MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+MomentProcessor::~MomentProcessor() = default;
+
+void MomentProcessor::StartProcessor() {}
+
+void MomentProcessor::SuspendProcessor() {}
+
+void MomentProcessor::StopProcessor() {}
+
+void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.window_of_interest = config.window_of_interest;
+ current_config.preprocess =
+ static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
+ current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h
new file mode 100644
index 000000000..d4bd22e0f
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/moment_processor.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class MomentProcessor final : public ProcessorBase {
+public:
+ explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~MomentProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
+
+private:
+ // This is nn::irsensor::MomentProcessorConfig
+ struct MomentProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::IrsRect window_of_interest;
+ Core::IrSensor::MomentProcessorPreprocess preprocess;
+ u32 preprocess_intensity_threshold;
+ };
+ static_assert(sizeof(MomentProcessorConfig) == 0x28,
+ "MomentProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::MomentStatistic
+ struct MomentStatistic {
+ f32 average_intensity;
+ Core::IrSensor::IrsCentroid centroid;
+ };
+ static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size");
+
+ // This is nn::irsensor::MomentProcessorState
+ struct MomentProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ INSERT_PADDING_BYTES(4);
+ std::array<MomentStatistic, 0x30> stadistic;
+ };
+ static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
+
+ MomentProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.cpp b/src/core/hle/service/hid/irsensor/pointing_processor.cpp
new file mode 100644
index 000000000..929f177fc
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/pointing_processor.cpp
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/pointing_processor.h"
+
+namespace Service::IRS {
+PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+PointingProcessor::~PointingProcessor() = default;
+
+void PointingProcessor::StartProcessor() {}
+
+void PointingProcessor::SuspendProcessor() {}
+
+void PointingProcessor::StopProcessor() {}
+
+void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) {
+ current_config.window_of_interest = config.window_of_interest;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h
new file mode 100644
index 000000000..cf4930794
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/pointing_processor.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class PointingProcessor final : public ProcessorBase {
+public:
+ explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~PointingProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config);
+
+private:
+ // This is nn::irsensor::PointingProcessorConfig
+ struct PointingProcessorConfig {
+ Core::IrSensor::IrsRect window_of_interest;
+ };
+ static_assert(sizeof(PointingProcessorConfig) == 0x8,
+ "PointingProcessorConfig is an invalid size");
+
+ struct PointingProcessorMarkerData {
+ u8 pointing_status;
+ INSERT_PADDING_BYTES(3);
+ u32 unknown;
+ float unkown_float1;
+ float position_x;
+ float position_y;
+ float unkown_float2;
+ Core::IrSensor::IrsRect window_of_interest;
+ };
+ static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
+ "PointingProcessorMarkerData is an invalid size");
+
+ struct PointingProcessorMarkerState {
+ s64 sampling_number;
+ u64 timestamp;
+ std::array<PointingProcessorMarkerData, 0x3> data;
+ };
+ static_assert(sizeof(PointingProcessorMarkerState) == 0x70,
+ "PointingProcessorMarkerState is an invalid size");
+
+ PointingProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/processor_base.cpp b/src/core/hle/service/hid/irsensor/processor_base.cpp
new file mode 100644
index 000000000..4d43ca17a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/processor_base.cpp
@@ -0,0 +1,67 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+
+ProcessorBase::ProcessorBase() {}
+ProcessorBase::~ProcessorBase() = default;
+
+bool ProcessorBase::IsProcessorActive() const {
+ return is_active;
+}
+
+std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 320 * 240;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 160 * 120;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 80 * 60;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 40 * 30;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 20 * 15;
+ default:
+ return 0;
+ }
+}
+
+std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 320;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 160;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 80;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 40;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 20;
+ default:
+ return 0;
+ }
+}
+
+std::size_t ProcessorBase::GetDataHeight(
+ Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 240;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 120;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 60;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 30;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 15;
+ default:
+ return 0;
+ }
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/processor_base.h b/src/core/hle/service/hid/irsensor/processor_base.h
new file mode 100644
index 000000000..bc0d2977b
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/processor_base.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+
+namespace Service::IRS {
+class ProcessorBase {
+public:
+ explicit ProcessorBase();
+ virtual ~ProcessorBase();
+
+ virtual void StartProcessor() = 0;
+ virtual void SuspendProcessor() = 0;
+ virtual void StopProcessor() = 0;
+
+ bool IsProcessorActive() const;
+
+protected:
+ /// Returns the number of bytes the image uses
+ std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ /// Returns the width of the image
+ std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ /// Returns the height of the image
+ std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ bool is_active{false};
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
new file mode 100644
index 000000000..e691c840a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
+
+namespace Service::IRS {
+TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+TeraPluginProcessor::~TeraPluginProcessor() = default;
+
+void TeraPluginProcessor::StartProcessor() {}
+
+void TeraPluginProcessor::SuspendProcessor() {}
+
+void TeraPluginProcessor::StopProcessor() {}
+
+void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) {
+ current_config.mode = config.mode;
+ current_config.unknown_1 = config.unknown_1;
+ current_config.unknown_2 = config.unknown_2;
+ current_config.unknown_3 = config.unknown_3;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.h b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h
new file mode 100644
index 000000000..bbea7ed0b
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class TeraPluginProcessor final : public ProcessorBase {
+public:
+ explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~TeraPluginProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config);
+
+private:
+ // This is nn::irsensor::TeraPluginProcessorConfig
+ struct TeraPluginProcessorConfig {
+ u8 mode;
+ u8 unknown_1;
+ u8 unknown_2;
+ u8 unknown_3;
+ };
+ static_assert(sizeof(TeraPluginProcessorConfig) == 0x4,
+ "TeraPluginProcessorConfig is an invalid size");
+
+ struct TeraPluginProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ std::array<u8, 0x12c> data;
+ };
+ static_assert(sizeof(TeraPluginProcessorState) == 0x140,
+ "TeraPluginProcessorState is an invalid size");
+
+ TeraPluginProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
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..0c07a7397
--- /dev/null
+++ b/src/core/hle/service/ldn/ldn_types.h
@@ -0,0 +1,284 @@
+// 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,
+ Excelent,
+};
+
+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;
+
+ std::string GetStringValue() const {
+ return std::string(raw.data(), length);
+ }
+};
+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/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 0310ce883..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 {
@@ -18,8 +17,8 @@ namespace {
} // Anonymous namespace
-#include "core/network/network.h"
-#include "core/network/network_interface.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
namespace Service::NIFM {
@@ -30,6 +29,19 @@ enum class RequestState : u32 {
Connected = 3,
};
+enum class InternetConnectionType : u8 {
+ WiFi = 1,
+ Ethernet = 2,
+};
+
+enum class InternetConnectionStatus : u8 {
+ ConnectingUnknown1,
+ ConnectingUnknown2,
+ ConnectingUnknown3,
+ ConnectingUnknown4,
+ Connected,
+};
+
struct IpAddressSetting {
bool is_automatic{};
Network::IPv4Address current_address{};
@@ -258,135 +270,45 @@ public:
}
};
-class IGeneralService final : public ServiceFramework<IGeneralService> {
-public:
- explicit IGeneralService(Core::System& system_);
-
-private:
- void GetClientId(Kernel::HLERequestContext& ctx) {
- static constexpr u32 client_id = 1;
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+void IGeneralService::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
- }
- void CreateScanRequest(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NIFM, "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, 2, 0, 1};
+void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IScanRequest>(system);
- }
- 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<IScanRequest>(system);
+}
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IRequest>(system);
- }
- void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
- 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},
- },
- .uuid{0xdeadbeef, 0xdeadbeef},
- .network_name{"yuzu Network"},
- .wireless_setting_data{
- .ssid_length{12},
- .ssid{"yuzu Network"},
- .passphrase{"yuzupassword"},
- },
- };
- }();
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- ctx.WriteBuffer(network_profile_data);
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IRequest>(system);
+}
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
- void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
- void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ const auto net_iface = Network::GetSelectedNetworkInterface();
- 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});
+ SfNetworkProfileData network_profile_data = [&net_iface] {
+ if (!net_iface) {
+ return SfNetworkProfileData{};
}
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushRaw(*ipv4);
- }
- void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NIFM, "called");
-
- 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, 6, 0, 1};
-
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<INetworkProfile>(system);
- rb.PushRaw<u128>(uuid);
- }
- void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
-
- struct IpConfigInfo {
- IpAddressSetting ip_address_setting{};
- DnsSetting dns_setting{};
- };
- static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
- "IpConfigInfo has incorrect size.");
-
- const auto net_iface = Network::GetSelectedNetworkInterface();
-
- const IpConfigInfo ip_config_info = [&net_iface] {
- if (!net_iface) {
- return IpConfigInfo{};
- }
-
- return IpConfigInfo{
+ return SfNetworkProfileData{
+ .ip_setting_data{
.ip_address_setting{
.is_automatic{true},
.current_address{Network::TranslateIPv4(net_iface->ip_address)},
@@ -398,46 +320,178 @@ private:
.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},
+ },
+ .uuid{0xdeadbeef, 0xdeadbeef},
+ .network_name{"yuzu Network"},
+ .wireless_setting_data{
+ .ssid_length{12},
+ .ssid{"yuzu Network"},
+ .passphrase{"yuzupassword"},
+ },
+ };
+ }();
- IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
- rb.Push(ResultSuccess);
- rb.PushRaw<IpConfigInfo>(ip_config_info);
+ // 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 IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(0);
+ ctx.WriteBuffer(network_profile_data);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+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 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);
+ // 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();
}
}
- void IsAnyInternetRequestAccepted(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);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(*ipv4);
+}
+
+void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
+
+ 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, 6, 0, 1};
+
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<INetworkProfile>(system);
+ rb.PushRaw<u128>(uuid);
+}
+
+void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ struct IpConfigInfo {
+ IpAddressSetting ip_address_setting{};
+ DnsSetting dns_setting{};
+ };
+ static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
+ "IpConfigInfo has incorrect size.");
+
+ const auto net_iface = Network::GetSelectedNetworkInterface();
+
+ IpConfigInfo ip_config_info = [&net_iface] {
+ if (!net_iface) {
+ return IpConfigInfo{};
+ }
+
+ 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},
+ },
+ };
+ }();
+
+ // 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();
}
}
-};
+
+ IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
+ rb.Push(ResultSuccess);
+ rb.PushRaw<IpConfigInfo>(ip_config_info);
+}
+
+void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u8>(1);
+}
+
+void IGeneralService::GetInternetConnectionStatus(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.");
+
+ 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 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);
+ }
+}
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"},
@@ -456,7 +510,7 @@ IGeneralService::IGeneralService(Core::System& system_)
{15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"},
{16, nullptr, "SetWirelessCommunicationEnabled"},
{17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
- {18, nullptr, "GetInternetConnectionStatus"},
+ {18, &IGeneralService::GetInternetConnectionStatus, "GetInternetConnectionStatus"},
{19, nullptr, "SetEthernetCommunicationEnabled"},
{20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"},
{21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"},
@@ -488,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/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h
index 21aaa40cd..157333ff8 100644
--- a/src/core/hle/service/nvflinger/binder.h
+++ b/src/core/hle/service/nvflinger/binder.h
@@ -34,6 +34,7 @@ enum class TransactionId {
class IBinder {
public:
+ virtual ~IBinder() = default;
virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
u32 flags) = 0;
virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 5f69c8c2c..5574269eb 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -287,9 +287,21 @@ s64 NVFlinger::GetNextTicks() const {
static constexpr s64 max_hertz = 120LL;
const auto& settings = Settings::values;
- const bool unlocked_fps = settings.disable_fps_limit.GetValue();
- const s64 fps_cap = unlocked_fps ? static_cast<s64>(settings.fps_cap.GetValue()) : 1;
- return (1000000000 * (1LL << swap_interval)) / (max_hertz * fps_cap);
+ auto speed_scale = 1.f;
+ if (settings.use_multi_core.GetValue()) {
+ if (settings.use_speed_limit.GetValue()) {
+ // Scales the speed based on speed_limit setting on MC. SC is handled by
+ // SpeedLimiter::DoSpeedLimiting.
+ speed_scale = 100.f / settings.speed_limit.GetValue();
+ } else {
+ // Run at unlocked framerate.
+ speed_scale = 0.01f;
+ }
+ }
+
+ const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz);
+
+ return static_cast<s64>(speed_scale * static_cast<float>(next_ticks));
}
} // namespace Service::NVFlinger
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/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
index 9e0eb6ac0..2c31e9485 100644
--- a/src/core/hle/service/ptm/psm.cpp
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -9,10 +9,8 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/ptm/psm.h"
-#include "core/hle/service/service.h"
-#include "core/hle/service/sm/sm.h"
-namespace Service::PSM {
+namespace Service::PTM {
class IPsmSession final : public ServiceFramework<IPsmSession> {
public:
@@ -57,7 +55,7 @@ public:
private:
void BindStateChangeEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ LOG_DEBUG(Service_PTM, "called");
should_signal = true;
@@ -67,7 +65,7 @@ private:
}
void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ LOG_DEBUG(Service_PTM, "called");
should_signal = false;
@@ -78,7 +76,7 @@ private:
void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_charger_type = state;
@@ -89,7 +87,7 @@ private:
void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_power_supply = state;
@@ -100,7 +98,7 @@ private:
void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_battery_voltage = state;
@@ -117,76 +115,58 @@ private:
Kernel::KEvent* state_change_event;
};
-class PSM final : public ServiceFramework<PSM> {
-public:
- explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
- {1, &PSM::GetChargerType, "GetChargerType"},
- {2, nullptr, "EnableBatteryCharging"},
- {3, nullptr, "DisableBatteryCharging"},
- {4, nullptr, "IsBatteryChargingEnabled"},
- {5, nullptr, "AcquireControllerPowerSupply"},
- {6, nullptr, "ReleaseControllerPowerSupply"},
- {7, &PSM::OpenSession, "OpenSession"},
- {8, nullptr, "EnableEnoughPowerChargeEmulation"},
- {9, nullptr, "DisableEnoughPowerChargeEmulation"},
- {10, nullptr, "EnableFastBatteryCharging"},
- {11, nullptr, "DisableFastBatteryCharging"},
- {12, nullptr, "GetBatteryVoltageState"},
- {13, nullptr, "GetRawBatteryChargePercentage"},
- {14, nullptr, "IsEnoughPowerSupplied"},
- {15, nullptr, "GetBatteryAgePercentage"},
- {16, nullptr, "GetBatteryChargeInfoEvent"},
- {17, nullptr, "GetBatteryChargeInfoFields"},
- {18, nullptr, "GetBatteryChargeCalibratedEvent"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
- ~PSM() override = default;
-
-private:
- void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+PSM::PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
+ {1, &PSM::GetChargerType, "GetChargerType"},
+ {2, nullptr, "EnableBatteryCharging"},
+ {3, nullptr, "DisableBatteryCharging"},
+ {4, nullptr, "IsBatteryChargingEnabled"},
+ {5, nullptr, "AcquireControllerPowerSupply"},
+ {6, nullptr, "ReleaseControllerPowerSupply"},
+ {7, &PSM::OpenSession, "OpenSession"},
+ {8, nullptr, "EnableEnoughPowerChargeEmulation"},
+ {9, nullptr, "DisableEnoughPowerChargeEmulation"},
+ {10, nullptr, "EnableFastBatteryCharging"},
+ {11, nullptr, "DisableFastBatteryCharging"},
+ {12, nullptr, "GetBatteryVoltageState"},
+ {13, nullptr, "GetRawBatteryChargePercentage"},
+ {14, nullptr, "IsEnoughPowerSupplied"},
+ {15, nullptr, "GetBatteryAgePercentage"},
+ {16, nullptr, "GetBatteryChargeInfoEvent"},
+ {17, nullptr, "GetBatteryChargeInfoFields"},
+ {18, nullptr, "GetBatteryChargeCalibratedEvent"},
+ };
+ // clang-format on
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(battery_charge_percentage);
- }
+ RegisterHandlers(functions);
+}
- void GetChargerType(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+PSM::~PSM() = default;
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(charger_type);
- }
+void PSM::GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
- void OpenSession(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(battery_charge_percentage);
+}
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IPsmSession>(system);
- }
+void PSM::GetChargerType(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
- enum class ChargerType : u32 {
- Unplugged = 0,
- RegularCharger = 1,
- LowPowerCharger = 2,
- Unknown = 3,
- };
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(charger_type);
+}
- u32 battery_charge_percentage{100}; // 100%
- ChargerType charger_type{ChargerType::RegularCharger};
-};
+void PSM::OpenSession(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
-void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
- std::make_shared<PSM>(system)->InstallAsService(sm);
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IPsmSession>(system);
}
-} // namespace Service::PSM
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h
index 94a1044db..f674ba8bc 100644
--- a/src/core/hle/service/ptm/psm.h
+++ b/src/core/hle/service/ptm/psm.h
@@ -3,16 +3,29 @@
#pragma once
-namespace Core {
-class System;
-}
+#include "core/hle/service/service.h"
-namespace Service::SM {
-class ServiceManager;
-}
+namespace Service::PTM {
-namespace Service::PSM {
+class PSM final : public ServiceFramework<PSM> {
+public:
+ explicit PSM(Core::System& system_);
+ ~PSM() override;
-void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
+private:
+ enum class ChargerType : u32 {
+ Unplugged = 0,
+ RegularCharger = 1,
+ LowPowerCharger = 2,
+ Unknown = 3,
+ };
-} // namespace Service::PSM
+ void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx);
+ void GetChargerType(Kernel::HLERequestContext& ctx);
+ void OpenSession(Kernel::HLERequestContext& ctx);
+
+ u32 battery_charge_percentage{100};
+ ChargerType charger_type{ChargerType::RegularCharger};
+};
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
new file mode 100644
index 000000000..4bea995c6
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+
+#include "core/core.h"
+#include "core/hle/service/ptm/psm.h"
+#include "core/hle/service/ptm/ptm.h"
+#include "core/hle/service/ptm/ts.h"
+
+namespace Service::PTM {
+
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
+ std::make_shared<PSM>(system)->InstallAsService(sm);
+ std::make_shared<TS>(system)->InstallAsService(sm);
+}
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
new file mode 100644
index 000000000..06224a24e
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.h
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+namespace Core {
+class System;
+}
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::PTM {
+
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp
new file mode 100644
index 000000000..65c3f135f
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.cpp
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+
+#include "core/core.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/ptm/ts.h"
+
+namespace Service::PTM {
+
+TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetTemperatureRange"},
+ {1, &TS::GetTemperature, "GetTemperature"},
+ {2, nullptr, "SetMeasurementMode"},
+ {3, nullptr, "GetTemperatureMilliC"},
+ {4, nullptr, "OpenSession"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+TS::~TS() = default;
+
+void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto location{rp.PopEnum<Location>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location);
+
+ const s32 temperature = location == Location::Internal ? 35 : 20;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(temperature);
+}
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h
new file mode 100644
index 000000000..39a734ef7
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::PTM {
+
+class TS final : public ServiceFramework<TS> {
+public:
+ explicit TS(Core::System& system_);
+ ~TS() override;
+
+private:
+ enum class Location : u8 {
+ Internal,
+ External,
+ };
+
+ void GetTemperature(Kernel::HLERequestContext& ctx);
+};
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 318009e4f..dadaf897f 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -58,7 +58,7 @@
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/prepo/prepo.h"
#include "core/hle/service/psc/psc.h"
-#include "core/hle/service/ptm/psm.h"
+#include "core/hle/service/ptm/ptm.h"
#include "core/hle/service/service.h"
#include "core/hle/service/set/settings.h"
#include "core/hle/service/sm/sm.h"
@@ -194,13 +194,16 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
Kernel::HLERequestContext& ctx) {
const auto guard = LockService();
+ Result result = ResultSuccess;
+
switch (ctx.GetCommandType()) {
case IPC::CommandType::Close:
case IPC::CommandType::TIPC_Close: {
session.Close();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
- return IPC::ERR_REMOTE_PROCESS_DEAD;
+ result = IPC::ERR_REMOTE_PROCESS_DEAD;
+ break;
}
case IPC::CommandType::ControlWithContext:
case IPC::CommandType::Control: {
@@ -227,7 +230,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
ctx.WriteToOutgoingCommandBuffer(ctx.GetThread());
}
- return ResultSuccess;
+ return result;
}
/// Initialize Services
@@ -287,7 +290,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
PlayReport::InstallInterfaces(*sm, system);
PM::InstallInterfaces(system);
PSC::InstallInterfaces(*sm, system);
- PSM::InstallInterfaces(*sm, system);
+ PTM::InstallInterfaces(*sm, system);
Set::InstallInterfaces(*sm, system);
Sockets::InstallInterfaces(*sm, system);
SPL::InstallInterfaces(*sm, system);
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 5114b8be2..e08c3cb67 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/network/network.h"
-#include "core/network/sockets.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));
@@ -720,7 +730,27 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message)
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
- return Translate(file_descriptors[fd]->socket->Recv(flags, 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) {
+ descriptor.socket->SetNonBlock(true);
+ }
+ }
+
+ const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
+
+ // Restore original state
+ if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
+ descriptor.socket->SetNonBlock(false);
+ }
+
+ return {ret, bsd_errno};
}
std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
@@ -741,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) {
@@ -839,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"},
@@ -881,6 +924,13 @@ 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;
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index fed740d87..81e855e0f 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -7,16 +7,19 @@
#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
namespace Service::Sockets {
@@ -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 9c0936d97..023aa0486 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -7,7 +7,7 @@
#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
#include "core/hle/service/sockets/sockets_translate.h"
-#include "core/network/network.h"
+#include "core/internal_network/network.h"
namespace Service::Sockets {
@@ -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/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h
index 5e9809add..c93291d3e 100644
--- a/src/core/hle/service/sockets/sockets_translate.h
+++ b/src/core/hle/service/sockets/sockets_translate.h
@@ -7,7 +7,7 @@
#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
-#include "core/network/network.h"
+#include "core/internal_network/network.h"
namespace Service::Sockets {
diff --git a/src/core/network/network.cpp b/src/core/internal_network/network.cpp
index fdafbea92..cdf38a2a4 100644
--- a/src/core/network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -29,9 +29,10 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
-#include "core/network/network.h"
-#include "core/network/network_interface.h"
-#include "core/network/sockets.h"
+#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/network/network.h b/src/core/internal_network/network.h
index 10e5ef10d..36994c22e 100644
--- a/src/core/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/network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 15ecc6abf..0f0a66160 100644
--- a/src/core/network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -11,7 +11,7 @@
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/string_util.h"
-#include "core/network/network_interface.h"
+#include "core/internal_network/network_interface.h"
#ifdef _WIN32
#include <iphlpapi.h>
diff --git a/src/core/network/network_interface.h b/src/core/internal_network/network_interface.h
index 9b98b6b42..9b98b6b42 100644
--- a/src/core/network/network_interface.h
+++ b/src/core/internal_network/network_interface.h
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
new file mode 100644
index 000000000..49d067f4c
--- /dev/null
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -0,0 +1,284 @@
+// 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;
+ }
+ 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;
+
+ 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
new file mode 100644
index 000000000..a70429b19
--- /dev/null
+++ b/src/core/internal_network/sockets.h
@@ -0,0 +1,163 @@
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#if defined(_WIN32)
+#elif !YUZU_UNIX
+#error "Platform not implemented"
+#endif
+
+#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 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<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;
+
+ 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;
+
+ Socket(Socket&& rhs) noexcept;
+
+ // Avoid closing sockets implicitly
+ Socket& operator=(Socket&&) noexcept = delete;
+
+ Errno Initialize(Domain domain, Type type, Protocol 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> Send(const std::vector<u8>& message, int flags) override;
+
+ 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 SetKeepAlive(bool enable) override;
+
+ Errno SetBroadcast(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;
+
+ void HandleProxyPacket(const ProxyPacket& packet) override;
+};
+
+std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
+
+} // namespace Network
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
deleted file mode 100644
index dfb10c34f..000000000
--- a/src/core/loader/elf.cpp
+++ /dev/null
@@ -1,263 +0,0 @@
-// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
-// SPDX-FileCopyrightText: 2014 Citra Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <cstring>
-#include <memory>
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-#include "common/elf.h"
-#include "common/logging/log.h"
-#include "core/hle/kernel/code_set.h"
-#include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/k_process.h"
-#include "core/loader/elf.h"
-#include "core/memory.h"
-
-using namespace Common::ELF;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// ElfReader class
-
-typedef int SectionID;
-
-class ElfReader {
-private:
- char* base;
- u32* base32;
-
- Elf32_Ehdr* header;
- Elf32_Phdr* segments;
- Elf32_Shdr* sections;
-
- u32* sectionAddrs;
- bool relocate;
- VAddr entryPoint;
-
-public:
- explicit ElfReader(void* ptr);
-
- u32 Read32(int off) const {
- return base32[off >> 2];
- }
-
- // Quick accessors
- u16 GetType() const {
- return header->e_type;
- }
- u16 GetMachine() const {
- return header->e_machine;
- }
- VAddr GetEntryPoint() const {
- return entryPoint;
- }
- u32 GetFlags() const {
- return (u32)(header->e_flags);
- }
- Kernel::CodeSet LoadInto(VAddr vaddr);
-
- int GetNumSegments() const {
- return (int)(header->e_phnum);
- }
- int GetNumSections() const {
- return (int)(header->e_shnum);
- }
- const u8* GetPtr(int offset) const {
- return (u8*)base + offset;
- }
- const char* GetSectionName(int section) const;
- const u8* GetSectionDataPtr(int section) const {
- if (section < 0 || section >= header->e_shnum)
- return nullptr;
- if (sections[section].sh_type != ElfShtNobits)
- return GetPtr(sections[section].sh_offset);
- else
- return nullptr;
- }
- bool IsCodeSection(int section) const {
- return sections[section].sh_type == ElfShtProgBits;
- }
- const u8* GetSegmentPtr(int segment) {
- return GetPtr(segments[segment].p_offset);
- }
- u32 GetSectionAddr(SectionID section) const {
- return sectionAddrs[section];
- }
- unsigned int GetSectionSize(SectionID section) const {
- return sections[section].sh_size;
- }
- SectionID GetSectionByName(const char* name, int firstSection = 0) const; //-1 for not found
-
- bool DidRelocate() const {
- return relocate;
- }
-};
-
-ElfReader::ElfReader(void* ptr) {
- base = (char*)ptr;
- base32 = (u32*)ptr;
- header = (Elf32_Ehdr*)ptr;
-
- segments = (Elf32_Phdr*)(base + header->e_phoff);
- sections = (Elf32_Shdr*)(base + header->e_shoff);
-
- entryPoint = header->e_entry;
-}
-
-const char* ElfReader::GetSectionName(int section) const {
- if (sections[section].sh_type == ElfShtNull)
- return nullptr;
-
- int name_offset = sections[section].sh_name;
- const char* ptr = reinterpret_cast<const char*>(GetSectionDataPtr(header->e_shstrndx));
-
- if (ptr)
- return ptr + name_offset;
-
- return nullptr;
-}
-
-Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
- LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
-
- // Should we relocate?
- relocate = (header->e_type != ElfTypeExec);
-
- if (relocate) {
- LOG_DEBUG(Loader, "Relocatable module");
- entryPoint += vaddr;
- } else {
- LOG_DEBUG(Loader, "Prerelocated executable");
- }
- LOG_DEBUG(Loader, "{} segments:", header->e_phnum);
-
- // First pass : Get the bits into RAM
- const VAddr base_addr = relocate ? vaddr : 0;
-
- u64 total_image_size = 0;
- for (unsigned int i = 0; i < header->e_phnum; ++i) {
- const Elf32_Phdr* p = &segments[i];
- if (p->p_type == ElfPtLoad) {
- total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
- }
- }
-
- Kernel::PhysicalMemory program_image(total_image_size);
- std::size_t current_image_position = 0;
-
- Kernel::CodeSet codeset;
-
- for (unsigned int i = 0; i < header->e_phnum; ++i) {
- const Elf32_Phdr* p = &segments[i];
- LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
- p->p_vaddr, p->p_filesz, p->p_memsz);
-
- if (p->p_type == ElfPtLoad) {
- Kernel::CodeSet::Segment* codeset_segment;
- u32 permission_flags = p->p_flags & (ElfPfRead | ElfPfWrite | ElfPfExec);
- if (permission_flags == (ElfPfRead | ElfPfExec)) {
- codeset_segment = &codeset.CodeSegment();
- } else if (permission_flags == (ElfPfRead)) {
- codeset_segment = &codeset.RODataSegment();
- } else if (permission_flags == (ElfPfRead | ElfPfWrite)) {
- codeset_segment = &codeset.DataSegment();
- } else {
- LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
- p->p_flags);
- continue;
- }
-
- if (codeset_segment->size != 0) {
- LOG_ERROR(Loader,
- "ELF has more than one segment of the same type. Skipping extra "
- "segment (id {})",
- i);
- continue;
- }
-
- const VAddr segment_addr = base_addr + p->p_vaddr;
- const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF;
-
- codeset_segment->offset = current_image_position;
- codeset_segment->addr = segment_addr;
- codeset_segment->size = aligned_size;
-
- std::memcpy(program_image.data() + current_image_position, GetSegmentPtr(i),
- p->p_filesz);
- current_image_position += aligned_size;
- }
- }
-
- codeset.entrypoint = base_addr + header->e_entry;
- codeset.memory = std::move(program_image);
-
- LOG_DEBUG(Loader, "Done loading.");
-
- return codeset;
-}
-
-SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const {
- for (int i = firstSection; i < header->e_shnum; i++) {
- const char* secname = GetSectionName(i);
-
- if (secname != nullptr && strcmp(name, secname) == 0)
- return i;
- }
- return -1;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Loader namespace
-
-namespace Loader {
-
-AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file_) : AppLoader(std::move(file_)) {}
-
-FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& elf_file) {
- static constexpr u16 ELF_MACHINE_ARM{0x28};
-
- u32 magic = 0;
- if (4 != elf_file->ReadObject(&magic)) {
- return FileType::Error;
- }
-
- u16 machine = 0;
- if (2 != elf_file->ReadObject(&machine, 18)) {
- return FileType::Error;
- }
-
- if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) {
- return FileType::ELF;
- }
-
- return FileType::Error;
-}
-
-AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::KProcess& process,
- [[maybe_unused]] Core::System& system) {
- if (is_loaded) {
- return {ResultStatus::ErrorAlreadyLoaded, {}};
- }
-
- std::vector<u8> buffer = file->ReadAllBytes();
- if (buffer.size() != file->GetSize()) {
- return {ResultStatus::ErrorIncorrectELFFileSize, {}};
- }
-
- const VAddr base_address = process.PageTable().GetCodeRegionStart();
- ElfReader elf_reader(&buffer[0]);
- Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
- const VAddr entry_point = codeset.entrypoint;
-
- // Setup the process code layout
- if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), buffer.size()).IsError()) {
- return {ResultStatus::ErrorNotInitialized, {}};
- }
-
- process.LoadModule(std::move(codeset), entry_point);
-
- is_loaded = true;
- return {ResultStatus::Success, LoadParameters{48, Core::Memory::DEFAULT_STACK_SIZE}};
-}
-
-} // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
deleted file mode 100644
index acd33dc3d..000000000
--- a/src/core/loader/elf.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
-// SPDX-FileCopyrightText: 2014 Citra Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/loader/loader.h"
-
-namespace Core {
-class System;
-}
-
-namespace Loader {
-
-/// Loads an ELF/AXF file
-class AppLoader_ELF final : public AppLoader {
-public:
- explicit AppLoader_ELF(FileSys::VirtualFile file);
-
- /**
- * Identifies whether or not the given file is an ELF file.
- *
- * @param elf_file The file to identify.
- *
- * @return FileType::ELF, or FileType::Error if the file is not an ELF file.
- */
- static FileType IdentifyType(const FileSys::VirtualFile& elf_file);
-
- FileType GetFileType() const override {
- return IdentifyType(file);
- }
-
- LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
-};
-
-} // namespace Loader
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/loader.cpp b/src/core/loader/loader.cpp
index 994ee891f..104d16efa 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -12,7 +12,6 @@
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/loader/deconstructed_rom_directory.h"
-#include "core/loader/elf.h"
#include "core/loader/kip.h"
#include "core/loader/nax.h"
#include "core/loader/nca.h"
@@ -39,8 +38,6 @@ std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
FileType IdentifyFile(FileSys::VirtualFile file) {
if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) {
return *romdir_type;
- } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) {
- return *elf_type;
} else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) {
return *nso_type;
} else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) {
@@ -69,8 +66,6 @@ FileType GuessFromFilename(const std::string& name) {
const std::string extension =
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
- if (extension == "elf")
- return FileType::ELF;
if (extension == "nro")
return FileType::NRO;
if (extension == "nso")
@@ -89,8 +84,6 @@ FileType GuessFromFilename(const std::string& name) {
std::string GetFileTypeString(FileType type) {
switch (type) {
- case FileType::ELF:
- return "ELF";
case FileType::NRO:
return "NRO";
case FileType::NSO:
@@ -208,10 +201,6 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
FileType type, u64 program_id,
std::size_t program_index) {
switch (type) {
- // Standard ELF file format.
- case FileType::ELF:
- return std::make_unique<AppLoader_ELF>(std::move(file));
-
// NX NSO file format.
case FileType::NSO:
return std::make_unique<AppLoader_NSO>(std::move(file));
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 7bf4faaf1..7b43f70ed 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -34,7 +34,6 @@ namespace Loader {
enum class FileType {
Error,
Unknown,
- ELF,
NSO,
NRO,
NCA,
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 584808d50..34ad7cadd 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
@@ -37,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);
@@ -48,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);
@@ -58,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 {};
@@ -68,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 {};
@@ -177,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) {
@@ -193,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;
}
@@ -340,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) {
@@ -355,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:
@@ -372,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:
@@ -399,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) {
@@ -412,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:
@@ -435,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;
}
@@ -466,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()) {
@@ -475,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);
}
}
}
@@ -486,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);
@@ -497,21 +499,21 @@ 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;
}
}
}
[[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const {
// AARCH64 masks the upper 16 bit of all memory accesses
- vaddr &= 0xffffffffffffLL;
+ vaddr &= 0xffffffffffffULL;
if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
on_unmapped();
@@ -519,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];
}
@@ -658,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;
}
@@ -669,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;
}
@@ -776,6 +778,10 @@ void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr s
impl->CopyBlock(process, dest_addr, src_addr, size);
}
+void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const std::size_t size) {
+ impl->ZeroBlock(process, dest_addr, size);
+}
+
void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
impl->RasterizerMarkRegionCached(vaddr, size, cached);
}
diff --git a/src/core/memory.h b/src/core/memory.h
index f22c0a2d8..a11ff8766 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -28,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 {
@@ -437,6 +436,19 @@ public:
std::size_t size);
/**
+ * Zeros a range of bytes within the current process' address space at the specified
+ * virtual address.
+ *
+ * @param process The process that will have data zeroed within its address space.
+ * @param dest_addr The destination virtual address to zero the data from.
+ * @param size The size of the range to zero out, in bytes.
+ *
+ * @post The range [dest_addr, size) within the process' address space contains the
+ * value 0.
+ */
+ void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
+
+ /**
* Marks each page within the specified address range as cached or uncached.
*
* @param vaddr The virtual address indicating the start of the address range.
diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h
deleted file mode 100644
index f889159f5..000000000
--- a/src/core/network/sockets.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <memory>
-#include <utility>
-
-#if defined(_WIN32)
-#elif !YUZU_UNIX
-#error "Platform not implemented"
-#endif
-
-#include "common/common_types.h"
-#include "core/network/network.h"
-
-// TODO: C++20 Replace std::vector usages with std::span
-
-namespace Network {
-
-class Socket {
-public:
- struct AcceptResult {
- std::unique_ptr<Socket> socket;
- SockAddrIn sockaddr_in;
- };
-
- explicit Socket() = default;
- ~Socket();
-
- Socket(const Socket&) = delete;
- Socket& operator=(const Socket&) = delete;
-
- Socket(Socket&& rhs) noexcept;
-
- // Avoid closing sockets implicitly
- Socket& operator=(Socket&&) noexcept = delete;
-
- Errno Initialize(Domain domain, Type type, Protocol protocol);
-
- Errno Close();
-
- std::pair<AcceptResult, Errno> Accept();
-
- Errno Connect(SockAddrIn addr_in);
-
- std::pair<SockAddrIn, Errno> GetPeerName();
-
- std::pair<SockAddrIn, Errno> GetSockName();
-
- Errno Bind(SockAddrIn addr);
-
- Errno Listen(s32 backlog);
-
- Errno Shutdown(ShutdownHow how);
-
- std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message);
-
- std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr);
-
- std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags);
-
- std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr);
-
- Errno SetLinger(bool enable, u32 linger);
-
- Errno SetReuseAddr(bool enable);
-
- Errno SetKeepAlive(bool enable);
-
- Errno SetBroadcast(bool enable);
-
- Errno SetSndBuf(u32 value);
-
- Errno SetRcvBuf(u32 value);
-
- Errno SetSndTimeo(u32 value);
-
- Errno SetRcvTimeo(u32 value);
-
- Errno SetNonBlock(bool enable);
-
- bool IsOpened() const;
-
-#if defined(_WIN32)
- SOCKET fd = INVALID_SOCKET;
-#elif YUZU_UNIX
- int fd = -1;
-#endif
-};
-
-std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
-
-} // namespace Network
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 6ef459b7a..f09c176f8 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <chrono>
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index 816202588..dd6becc02 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 654db0b52..abcf6eb11 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 6f3d45bea..887dc98f3 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once