diff options
Diffstat (limited to 'src/core')
90 files changed, 6148 insertions, 2555 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 33cf470d5..95302c419 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -138,8 +138,6 @@ add_library(core STATIC | |||
| 138 | frontend/emu_window.h | 138 | frontend/emu_window.h |
| 139 | frontend/framebuffer_layout.cpp | 139 | frontend/framebuffer_layout.cpp |
| 140 | frontend/framebuffer_layout.h | 140 | frontend/framebuffer_layout.h |
| 141 | hardware_interrupt_manager.cpp | ||
| 142 | hardware_interrupt_manager.h | ||
| 143 | hid/emulated_console.cpp | 141 | hid/emulated_console.cpp |
| 144 | hid/emulated_console.h | 142 | hid/emulated_console.h |
| 145 | hid/emulated_controller.cpp | 143 | hid/emulated_controller.cpp |
| @@ -460,6 +458,8 @@ add_library(core STATIC | |||
| 460 | hle/service/hid/controllers/mouse.h | 458 | hle/service/hid/controllers/mouse.h |
| 461 | hle/service/hid/controllers/npad.cpp | 459 | hle/service/hid/controllers/npad.cpp |
| 462 | hle/service/hid/controllers/npad.h | 460 | hle/service/hid/controllers/npad.h |
| 461 | hle/service/hid/controllers/palma.cpp | ||
| 462 | hle/service/hid/controllers/palma.h | ||
| 463 | hle/service/hid/controllers/stubbed.cpp | 463 | hle/service/hid/controllers/stubbed.cpp |
| 464 | hle/service/hid/controllers/stubbed.h | 464 | hle/service/hid/controllers/stubbed.h |
| 465 | hle/service/hid/controllers/touchscreen.cpp | 465 | hle/service/hid/controllers/touchscreen.cpp |
| @@ -494,6 +494,8 @@ add_library(core STATIC | |||
| 494 | hle/service/jit/jit.h | 494 | hle/service/jit/jit.h |
| 495 | hle/service/lbl/lbl.cpp | 495 | hle/service/lbl/lbl.cpp |
| 496 | hle/service/lbl/lbl.h | 496 | hle/service/lbl/lbl.h |
| 497 | hle/service/ldn/lan_discovery.cpp | ||
| 498 | hle/service/ldn/lan_discovery.h | ||
| 497 | hle/service/ldn/ldn_results.h | 499 | hle/service/ldn/ldn_results.h |
| 498 | hle/service/ldn/ldn.cpp | 500 | hle/service/ldn/ldn.cpp |
| 499 | hle/service/ldn/ldn.h | 501 | hle/service/ldn/ldn.h |
| @@ -521,9 +523,12 @@ add_library(core STATIC | |||
| 521 | hle/service/nfc/nfc.h | 523 | hle/service/nfc/nfc.h |
| 522 | hle/service/nfp/amiibo_crypto.cpp | 524 | hle/service/nfp/amiibo_crypto.cpp |
| 523 | hle/service/nfp/amiibo_crypto.h | 525 | hle/service/nfp/amiibo_crypto.h |
| 524 | hle/service/nfp/amiibo_types.h | ||
| 525 | hle/service/nfp/nfp.cpp | 526 | hle/service/nfp/nfp.cpp |
| 526 | hle/service/nfp/nfp.h | 527 | hle/service/nfp/nfp.h |
| 528 | hle/service/nfp/nfp_device.cpp | ||
| 529 | hle/service/nfp/nfp_device.h | ||
| 530 | hle/service/nfp/nfp_result.h | ||
| 531 | hle/service/nfp/nfp_types.h | ||
| 527 | hle/service/nfp/nfp_user.cpp | 532 | hle/service/nfp/nfp_user.cpp |
| 528 | hle/service/nfp/nfp_user.h | 533 | hle/service/nfp/nfp_user.h |
| 529 | hle/service/ngct/ngct.cpp | 534 | hle/service/ngct/ngct.cpp |
| @@ -543,6 +548,12 @@ add_library(core STATIC | |||
| 543 | hle/service/ns/ns.h | 548 | hle/service/ns/ns.h |
| 544 | hle/service/ns/pdm_qry.cpp | 549 | hle/service/ns/pdm_qry.cpp |
| 545 | hle/service/ns/pdm_qry.h | 550 | hle/service/ns/pdm_qry.h |
| 551 | hle/service/nvdrv/core/container.cpp | ||
| 552 | hle/service/nvdrv/core/container.h | ||
| 553 | hle/service/nvdrv/core/nvmap.cpp | ||
| 554 | hle/service/nvdrv/core/nvmap.h | ||
| 555 | hle/service/nvdrv/core/syncpoint_manager.cpp | ||
| 556 | hle/service/nvdrv/core/syncpoint_manager.h | ||
| 546 | hle/service/nvdrv/devices/nvdevice.h | 557 | hle/service/nvdrv/devices/nvdevice.h |
| 547 | hle/service/nvdrv/devices/nvdisp_disp0.cpp | 558 | hle/service/nvdrv/devices/nvdisp_disp0.cpp |
| 548 | hle/service/nvdrv/devices/nvdisp_disp0.h | 559 | hle/service/nvdrv/devices/nvdisp_disp0.h |
| @@ -571,8 +582,6 @@ add_library(core STATIC | |||
| 571 | hle/service/nvdrv/nvdrv_interface.h | 582 | hle/service/nvdrv/nvdrv_interface.h |
| 572 | hle/service/nvdrv/nvmemp.cpp | 583 | hle/service/nvdrv/nvmemp.cpp |
| 573 | hle/service/nvdrv/nvmemp.h | 584 | hle/service/nvdrv/nvmemp.h |
| 574 | hle/service/nvdrv/syncpoint_manager.cpp | ||
| 575 | hle/service/nvdrv/syncpoint_manager.h | ||
| 576 | hle/service/nvflinger/binder.h | 585 | hle/service/nvflinger/binder.h |
| 577 | hle/service/nvflinger/buffer_item.h | 586 | hle/service/nvflinger/buffer_item.h |
| 578 | hle/service/nvflinger/buffer_item_consumer.cpp | 587 | hle/service/nvflinger/buffer_item_consumer.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 121092868..1deeee154 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -27,7 +27,6 @@ | |||
| 27 | #include "core/file_sys/savedata_factory.h" | 27 | #include "core/file_sys/savedata_factory.h" |
| 28 | #include "core/file_sys/vfs_concat.h" | 28 | #include "core/file_sys/vfs_concat.h" |
| 29 | #include "core/file_sys/vfs_real.h" | 29 | #include "core/file_sys/vfs_real.h" |
| 30 | #include "core/hardware_interrupt_manager.h" | ||
| 31 | #include "core/hid/hid_core.h" | 30 | #include "core/hid/hid_core.h" |
| 32 | #include "core/hle/kernel/k_memory_manager.h" | 31 | #include "core/hle/kernel/k_memory_manager.h" |
| 33 | #include "core/hle/kernel/k_process.h" | 32 | #include "core/hle/kernel/k_process.h" |
| @@ -51,6 +50,7 @@ | |||
| 51 | #include "core/telemetry_session.h" | 50 | #include "core/telemetry_session.h" |
| 52 | #include "core/tools/freezer.h" | 51 | #include "core/tools/freezer.h" |
| 53 | #include "network/network.h" | 52 | #include "network/network.h" |
| 53 | #include "video_core/host1x/host1x.h" | ||
| 54 | #include "video_core/renderer_base.h" | 54 | #include "video_core/renderer_base.h" |
| 55 | #include "video_core/video_core.h" | 55 | #include "video_core/video_core.h" |
| 56 | 56 | ||
| @@ -215,6 +215,7 @@ struct System::Impl { | |||
| 215 | 215 | ||
| 216 | telemetry_session = std::make_unique<Core::TelemetrySession>(); | 216 | telemetry_session = std::make_unique<Core::TelemetrySession>(); |
| 217 | 217 | ||
| 218 | host1x_core = std::make_unique<Tegra::Host1x::Host1x>(system); | ||
| 218 | gpu_core = VideoCore::CreateGPU(emu_window, system); | 219 | gpu_core = VideoCore::CreateGPU(emu_window, system); |
| 219 | if (!gpu_core) { | 220 | if (!gpu_core) { |
| 220 | return SystemResultStatus::ErrorVideoCore; | 221 | return SystemResultStatus::ErrorVideoCore; |
| @@ -224,7 +225,6 @@ struct System::Impl { | |||
| 224 | 225 | ||
| 225 | service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); | 226 | service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); |
| 226 | services = std::make_unique<Service::Services>(service_manager, system); | 227 | services = std::make_unique<Service::Services>(service_manager, system); |
| 227 | interrupt_manager = std::make_unique<Hardware::InterruptManager>(system); | ||
| 228 | 228 | ||
| 229 | // Initialize time manager, which must happen after kernel is created | 229 | // Initialize time manager, which must happen after kernel is created |
| 230 | time_manager.Initialize(); | 230 | time_manager.Initialize(); |
| @@ -373,6 +373,7 @@ struct System::Impl { | |||
| 373 | app_loader.reset(); | 373 | app_loader.reset(); |
| 374 | audio_core.reset(); | 374 | audio_core.reset(); |
| 375 | gpu_core.reset(); | 375 | gpu_core.reset(); |
| 376 | host1x_core.reset(); | ||
| 376 | perf_stats.reset(); | 377 | perf_stats.reset(); |
| 377 | kernel.Shutdown(); | 378 | kernel.Shutdown(); |
| 378 | memory.Reset(); | 379 | memory.Reset(); |
| @@ -450,7 +451,7 @@ struct System::Impl { | |||
| 450 | /// AppLoader used to load the current executing application | 451 | /// AppLoader used to load the current executing application |
| 451 | std::unique_ptr<Loader::AppLoader> app_loader; | 452 | std::unique_ptr<Loader::AppLoader> app_loader; |
| 452 | std::unique_ptr<Tegra::GPU> gpu_core; | 453 | std::unique_ptr<Tegra::GPU> gpu_core; |
| 453 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; | 454 | std::unique_ptr<Tegra::Host1x::Host1x> host1x_core; |
| 454 | std::unique_ptr<Core::DeviceMemory> device_memory; | 455 | std::unique_ptr<Core::DeviceMemory> device_memory; |
| 455 | std::unique_ptr<AudioCore::AudioCore> audio_core; | 456 | std::unique_ptr<AudioCore::AudioCore> audio_core; |
| 456 | Core::Memory::Memory memory; | 457 | Core::Memory::Memory memory; |
| @@ -668,12 +669,12 @@ const Tegra::GPU& System::GPU() const { | |||
| 668 | return *impl->gpu_core; | 669 | return *impl->gpu_core; |
| 669 | } | 670 | } |
| 670 | 671 | ||
| 671 | Core::Hardware::InterruptManager& System::InterruptManager() { | 672 | Tegra::Host1x::Host1x& System::Host1x() { |
| 672 | return *impl->interrupt_manager; | 673 | return *impl->host1x_core; |
| 673 | } | 674 | } |
| 674 | 675 | ||
| 675 | const Core::Hardware::InterruptManager& System::InterruptManager() const { | 676 | const Tegra::Host1x::Host1x& System::Host1x() const { |
| 676 | return *impl->interrupt_manager; | 677 | return *impl->host1x_core; |
| 677 | } | 678 | } |
| 678 | 679 | ||
| 679 | VideoCore::RendererBase& System::Renderer() { | 680 | VideoCore::RendererBase& System::Renderer() { |
diff --git a/src/core/core.h b/src/core/core.h index 0ce3b1d60..7843cc8ad 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -74,6 +74,9 @@ class TimeManager; | |||
| 74 | namespace Tegra { | 74 | namespace Tegra { |
| 75 | class DebugContext; | 75 | class DebugContext; |
| 76 | class GPU; | 76 | class GPU; |
| 77 | namespace Host1x { | ||
| 78 | class Host1x; | ||
| 79 | } // namespace Host1x | ||
| 77 | } // namespace Tegra | 80 | } // namespace Tegra |
| 78 | 81 | ||
| 79 | namespace VideoCore { | 82 | namespace VideoCore { |
| @@ -88,10 +91,6 @@ namespace Core::Timing { | |||
| 88 | class CoreTiming; | 91 | class CoreTiming; |
| 89 | } | 92 | } |
| 90 | 93 | ||
| 91 | namespace Core::Hardware { | ||
| 92 | class InterruptManager; | ||
| 93 | } | ||
| 94 | |||
| 95 | namespace Core::HID { | 94 | namespace Core::HID { |
| 96 | class HIDCore; | 95 | class HIDCore; |
| 97 | } | 96 | } |
| @@ -260,6 +259,12 @@ public: | |||
| 260 | /// Gets an immutable reference to the GPU interface. | 259 | /// Gets an immutable reference to the GPU interface. |
| 261 | [[nodiscard]] const Tegra::GPU& GPU() const; | 260 | [[nodiscard]] const Tegra::GPU& GPU() const; |
| 262 | 261 | ||
| 262 | /// Gets a mutable reference to the Host1x interface | ||
| 263 | [[nodiscard]] Tegra::Host1x::Host1x& Host1x(); | ||
| 264 | |||
| 265 | /// Gets an immutable reference to the Host1x interface. | ||
| 266 | [[nodiscard]] const Tegra::Host1x::Host1x& Host1x() const; | ||
| 267 | |||
| 263 | /// Gets a mutable reference to the renderer. | 268 | /// Gets a mutable reference to the renderer. |
| 264 | [[nodiscard]] VideoCore::RendererBase& Renderer(); | 269 | [[nodiscard]] VideoCore::RendererBase& Renderer(); |
| 265 | 270 | ||
| @@ -296,12 +301,6 @@ public: | |||
| 296 | /// Provides a constant reference to the core timing instance. | 301 | /// Provides a constant reference to the core timing instance. |
| 297 | [[nodiscard]] const Timing::CoreTiming& CoreTiming() const; | 302 | [[nodiscard]] const Timing::CoreTiming& CoreTiming() const; |
| 298 | 303 | ||
| 299 | /// Provides a reference to the interrupt manager instance. | ||
| 300 | [[nodiscard]] Core::Hardware::InterruptManager& InterruptManager(); | ||
| 301 | |||
| 302 | /// Provides a constant reference to the interrupt manager instance. | ||
| 303 | [[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const; | ||
| 304 | |||
| 305 | /// Provides a reference to the kernel instance. | 304 | /// Provides a reference to the kernel instance. |
| 306 | [[nodiscard]] Kernel::KernelCore& Kernel(); | 305 | [[nodiscard]] Kernel::KernelCore& Kernel(); |
| 307 | 306 | ||
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index f6c4567ba..6c0fcb7b5 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -43,7 +43,7 @@ CoreTiming::CoreTiming() | |||
| 43 | CoreTiming::~CoreTiming() = default; | 43 | CoreTiming::~CoreTiming() = default; |
| 44 | 44 | ||
| 45 | void CoreTiming::ThreadEntry(CoreTiming& instance) { | 45 | void CoreTiming::ThreadEntry(CoreTiming& instance) { |
| 46 | constexpr char name[] = "yuzu:HostTiming"; | 46 | constexpr char name[] = "HostTiming"; |
| 47 | MicroProfileOnThreadCreate(name); | 47 | MicroProfileOnThreadCreate(name); |
| 48 | Common::SetCurrentThreadName(name); | 48 | Common::SetCurrentThreadName(name); |
| 49 | Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); | 49 | Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); |
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 9b1565ae1..0dd4c2196 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -189,9 +189,9 @@ void CpuManager::RunThread(std::size_t core) { | |||
| 189 | system.RegisterCoreThread(core); | 189 | system.RegisterCoreThread(core); |
| 190 | std::string name; | 190 | std::string name; |
| 191 | if (is_multicore) { | 191 | if (is_multicore) { |
| 192 | name = "yuzu:CPUCore_" + std::to_string(core); | 192 | name = "CPUCore_" + std::to_string(core); |
| 193 | } else { | 193 | } else { |
| 194 | name = "yuzu:CPUThread"; | 194 | name = "CPUThread"; |
| 195 | } | 195 | } |
| 196 | MicroProfileOnThreadCreate(name.c_str()); | 196 | MicroProfileOnThreadCreate(name.c_str()); |
| 197 | Common::SetCurrentThreadName(name.c_str()); | 197 | Common::SetCurrentThreadName(name.c_str()); |
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index e42bdd17d..339f971e6 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp | |||
| @@ -140,7 +140,7 @@ private: | |||
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | void ThreadLoop(std::stop_token stop_token) { | 142 | void ThreadLoop(std::stop_token stop_token) { |
| 143 | Common::SetCurrentThreadName("yuzu:Debugger"); | 143 | Common::SetCurrentThreadName("Debugger"); |
| 144 | 144 | ||
| 145 | // Set up the client signals for new data. | 145 | // Set up the client signals for new data. |
| 146 | AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); | 146 | AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); |
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp deleted file mode 100644 index d08cc3315..000000000 --- a/src/core/hardware_interrupt_manager.cpp +++ /dev/null | |||
| @@ -1,32 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hardware_interrupt_manager.h" | ||
| 7 | #include "core/hle/service/nvdrv/nvdrv_interface.h" | ||
| 8 | #include "core/hle/service/sm/sm.h" | ||
| 9 | |||
| 10 | namespace Core::Hardware { | ||
| 11 | |||
| 12 | InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { | ||
| 13 | gpu_interrupt_event = Core::Timing::CreateEvent( | ||
| 14 | "GPUInterrupt", | ||
| 15 | [this](std::uintptr_t message, u64 time, | ||
| 16 | std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { | ||
| 17 | auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); | ||
| 18 | const u32 syncpt = static_cast<u32>(message >> 32); | ||
| 19 | const u32 value = static_cast<u32>(message); | ||
| 20 | nvdrv->SignalGPUInterruptSyncpt(syncpt, value); | ||
| 21 | return std::nullopt; | ||
| 22 | }); | ||
| 23 | } | ||
| 24 | |||
| 25 | InterruptManager::~InterruptManager() = default; | ||
| 26 | |||
| 27 | void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { | ||
| 28 | const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value; | ||
| 29 | system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg); | ||
| 30 | } | ||
| 31 | |||
| 32 | } // namespace Core::Hardware | ||
diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h deleted file mode 100644 index 5665c5918..000000000 --- a/src/core/hardware_interrupt_manager.h +++ /dev/null | |||
| @@ -1,32 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace Core { | ||
| 11 | class System; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Core::Timing { | ||
| 15 | struct EventType; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Core::Hardware { | ||
| 19 | |||
| 20 | class InterruptManager { | ||
| 21 | public: | ||
| 22 | explicit InterruptManager(Core::System& system); | ||
| 23 | ~InterruptManager(); | ||
| 24 | |||
| 25 | void GPUInterruptSyncpt(u32 syncpoint_id, u32 value); | ||
| 26 | |||
| 27 | private: | ||
| 28 | Core::System& system; | ||
| 29 | std::shared_ptr<Core::Timing::EventType> gpu_interrupt_event; | ||
| 30 | }; | ||
| 31 | |||
| 32 | } // namespace Core::Hardware | ||
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 01c43be93..025f1c78e 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -93,7 +93,7 @@ void EmulatedController::ReloadFromSettings() { | |||
| 93 | .body = GetNpadColor(player.body_color_left), | 93 | .body = GetNpadColor(player.body_color_left), |
| 94 | .button = GetNpadColor(player.button_color_left), | 94 | .button = GetNpadColor(player.button_color_left), |
| 95 | }; | 95 | }; |
| 96 | controller.colors_state.left = { | 96 | controller.colors_state.right = { |
| 97 | .body = GetNpadColor(player.body_color_right), | 97 | .body = GetNpadColor(player.body_color_right), |
| 98 | .button = GetNpadColor(player.button_color_right), | 98 | .button = GetNpadColor(player.button_color_right), |
| 99 | }; | 99 | }; |
| @@ -131,13 +131,16 @@ void EmulatedController::LoadDevices() { | |||
| 131 | battery_params[RightIndex].Set("battery", true); | 131 | battery_params[RightIndex].Set("battery", true); |
| 132 | 132 | ||
| 133 | camera_params = Common::ParamPackage{"engine:camera,camera:1"}; | 133 | camera_params = Common::ParamPackage{"engine:camera,camera:1"}; |
| 134 | nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; | ||
| 134 | 135 | ||
| 135 | output_params[LeftIndex] = left_joycon; | 136 | output_params[LeftIndex] = left_joycon; |
| 136 | output_params[RightIndex] = right_joycon; | 137 | output_params[RightIndex] = right_joycon; |
| 137 | output_params[2] = camera_params; | 138 | output_params[2] = camera_params; |
| 139 | output_params[3] = nfc_params; | ||
| 138 | output_params[LeftIndex].Set("output", true); | 140 | output_params[LeftIndex].Set("output", true); |
| 139 | output_params[RightIndex].Set("output", true); | 141 | output_params[RightIndex].Set("output", true); |
| 140 | output_params[2].Set("output", true); | 142 | output_params[2].Set("output", true); |
| 143 | output_params[3].Set("output", true); | ||
| 141 | 144 | ||
| 142 | LoadTASParams(); | 145 | LoadTASParams(); |
| 143 | 146 | ||
| @@ -155,6 +158,7 @@ void EmulatedController::LoadDevices() { | |||
| 155 | std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(), | 158 | std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(), |
| 156 | Common::Input::CreateDevice<Common::Input::InputDevice>); | 159 | Common::Input::CreateDevice<Common::Input::InputDevice>); |
| 157 | camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params); | 160 | camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params); |
| 161 | nfc_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(nfc_params); | ||
| 158 | std::transform(output_params.begin(), output_params.end(), output_devices.begin(), | 162 | std::transform(output_params.begin(), output_params.end(), output_devices.begin(), |
| 159 | Common::Input::CreateDevice<Common::Input::OutputDevice>); | 163 | Common::Input::CreateDevice<Common::Input::OutputDevice>); |
| 160 | 164 | ||
| @@ -284,6 +288,16 @@ void EmulatedController::ReloadInput() { | |||
| 284 | camera_devices->ForceUpdate(); | 288 | camera_devices->ForceUpdate(); |
| 285 | } | 289 | } |
| 286 | 290 | ||
| 291 | if (nfc_devices) { | ||
| 292 | if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) { | ||
| 293 | nfc_devices->SetCallback({ | ||
| 294 | .on_change = | ||
| 295 | [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); }, | ||
| 296 | }); | ||
| 297 | nfc_devices->ForceUpdate(); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 287 | // Use a common UUID for TAS | 301 | // Use a common UUID for TAS |
| 288 | static constexpr Common::UUID TAS_UUID = Common::UUID{ | 302 | static constexpr Common::UUID TAS_UUID = Common::UUID{ |
| 289 | {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; | 303 | {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; |
| @@ -339,6 +353,8 @@ void EmulatedController::UnloadInput() { | |||
| 339 | for (auto& stick : tas_stick_devices) { | 353 | for (auto& stick : tas_stick_devices) { |
| 340 | stick.reset(); | 354 | stick.reset(); |
| 341 | } | 355 | } |
| 356 | camera_devices.reset(); | ||
| 357 | nfc_devices.reset(); | ||
| 342 | } | 358 | } |
| 343 | 359 | ||
| 344 | void EmulatedController::EnableConfiguration() { | 360 | void EmulatedController::EnableConfiguration() { |
| @@ -903,6 +919,25 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback | |||
| 903 | TriggerOnChange(ControllerTriggerType::IrSensor, true); | 919 | TriggerOnChange(ControllerTriggerType::IrSensor, true); |
| 904 | } | 920 | } |
| 905 | 921 | ||
| 922 | void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { | ||
| 923 | std::unique_lock lock{mutex}; | ||
| 924 | controller.nfc_values = TransformToNfc(callback); | ||
| 925 | |||
| 926 | if (is_configuring) { | ||
| 927 | lock.unlock(); | ||
| 928 | TriggerOnChange(ControllerTriggerType::Nfc, false); | ||
| 929 | return; | ||
| 930 | } | ||
| 931 | |||
| 932 | controller.nfc_state = { | ||
| 933 | controller.nfc_values.state, | ||
| 934 | controller.nfc_values.data, | ||
| 935 | }; | ||
| 936 | |||
| 937 | lock.unlock(); | ||
| 938 | TriggerOnChange(ControllerTriggerType::Nfc, true); | ||
| 939 | } | ||
| 940 | |||
| 906 | bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { | 941 | bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { |
| 907 | if (device_index >= output_devices.size()) { | 942 | if (device_index >= output_devices.size()) { |
| 908 | return false; | 943 | return false; |
| @@ -980,7 +1015,13 @@ bool EmulatedController::TestVibration(std::size_t device_index) { | |||
| 980 | bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { | 1015 | bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { |
| 981 | LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); | 1016 | LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); |
| 982 | auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | 1017 | auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; |
| 983 | return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; | 1018 | auto& nfc_output_device = output_devices[3]; |
| 1019 | |||
| 1020 | const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); | ||
| 1021 | const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode); | ||
| 1022 | |||
| 1023 | return virtual_nfc_result == Common::Input::PollingError::None || | ||
| 1024 | mapped_nfc_result == Common::Input::PollingError::None; | ||
| 984 | } | 1025 | } |
| 985 | 1026 | ||
| 986 | bool EmulatedController::SetCameraFormat( | 1027 | bool EmulatedController::SetCameraFormat( |
| @@ -1000,6 +1041,32 @@ bool EmulatedController::SetCameraFormat( | |||
| 1000 | camera_format)) == Common::Input::CameraError::None; | 1041 | camera_format)) == Common::Input::CameraError::None; |
| 1001 | } | 1042 | } |
| 1002 | 1043 | ||
| 1044 | bool EmulatedController::HasNfc() const { | ||
| 1045 | const auto& nfc_output_device = output_devices[3]; | ||
| 1046 | |||
| 1047 | switch (npad_type) { | ||
| 1048 | case NpadStyleIndex::JoyconRight: | ||
| 1049 | case NpadStyleIndex::JoyconDual: | ||
| 1050 | case NpadStyleIndex::ProController: | ||
| 1051 | break; | ||
| 1052 | default: | ||
| 1053 | return false; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | const bool has_virtual_nfc = | ||
| 1057 | npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld; | ||
| 1058 | const bool is_virtual_nfc_supported = | ||
| 1059 | nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported; | ||
| 1060 | |||
| 1061 | return is_connected && (has_virtual_nfc && is_virtual_nfc_supported); | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | bool EmulatedController::WriteNfc(const std::vector<u8>& data) { | ||
| 1065 | auto& nfc_output_device = output_devices[3]; | ||
| 1066 | |||
| 1067 | return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; | ||
| 1068 | } | ||
| 1069 | |||
| 1003 | void EmulatedController::SetLedPattern() { | 1070 | void EmulatedController::SetLedPattern() { |
| 1004 | for (auto& device : output_devices) { | 1071 | for (auto& device : output_devices) { |
| 1005 | if (!device) { | 1072 | if (!device) { |
| @@ -1363,6 +1430,11 @@ const CameraState& EmulatedController::GetCamera() const { | |||
| 1363 | return controller.camera_state; | 1430 | return controller.camera_state; |
| 1364 | } | 1431 | } |
| 1365 | 1432 | ||
| 1433 | const NfcState& EmulatedController::GetNfc() const { | ||
| 1434 | std::scoped_lock lock{mutex}; | ||
| 1435 | return controller.nfc_state; | ||
| 1436 | } | ||
| 1437 | |||
| 1366 | NpadColor EmulatedController::GetNpadColor(u32 color) { | 1438 | NpadColor EmulatedController::GetNpadColor(u32 color) { |
| 1367 | return { | 1439 | return { |
| 1368 | .r = static_cast<u8>((color >> 16) & 0xFF), | 1440 | .r = static_cast<u8>((color >> 16) & 0xFF), |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index c3aa8f9d3..319226bf8 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -20,7 +20,7 @@ | |||
| 20 | 20 | ||
| 21 | namespace Core::HID { | 21 | namespace Core::HID { |
| 22 | const std::size_t max_emulated_controllers = 2; | 22 | const std::size_t max_emulated_controllers = 2; |
| 23 | const std::size_t output_devices = 3; | 23 | const std::size_t output_devices_size = 4; |
| 24 | struct ControllerMotionInfo { | 24 | struct ControllerMotionInfo { |
| 25 | Common::Input::MotionStatus raw_status{}; | 25 | Common::Input::MotionStatus raw_status{}; |
| 26 | MotionInput emulated{}; | 26 | MotionInput emulated{}; |
| @@ -37,7 +37,8 @@ using TriggerDevices = | |||
| 37 | using BatteryDevices = | 37 | using BatteryDevices = |
| 38 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | 38 | std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; |
| 39 | using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; | 39 | using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; |
| 40 | using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>; | 40 | using NfcDevices = std::unique_ptr<Common::Input::InputDevice>; |
| 41 | using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; | ||
| 41 | 42 | ||
| 42 | using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; | 43 | using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; |
| 43 | using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; | 44 | using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; |
| @@ -45,7 +46,8 @@ using ControllerMotionParams = std::array<Common::ParamPackage, Settings::Native | |||
| 45 | using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; | 46 | using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; |
| 46 | using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; | 47 | using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; |
| 47 | using CameraParams = Common::ParamPackage; | 48 | using CameraParams = Common::ParamPackage; |
| 48 | using OutputParams = std::array<Common::ParamPackage, output_devices>; | 49 | using NfcParams = Common::ParamPackage; |
| 50 | using OutputParams = std::array<Common::ParamPackage, output_devices_size>; | ||
| 49 | 51 | ||
| 50 | using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; | 52 | using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; |
| 51 | using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; | 53 | using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; |
| @@ -55,6 +57,7 @@ using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::Native | |||
| 55 | using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; | 57 | using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; |
| 56 | using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; | 58 | using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; |
| 57 | using CameraValues = Common::Input::CameraStatus; | 59 | using CameraValues = Common::Input::CameraStatus; |
| 60 | using NfcValues = Common::Input::NfcStatus; | ||
| 58 | using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; | 61 | using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; |
| 59 | 62 | ||
| 60 | struct AnalogSticks { | 63 | struct AnalogSticks { |
| @@ -80,6 +83,11 @@ struct CameraState { | |||
| 80 | std::size_t sample{}; | 83 | std::size_t sample{}; |
| 81 | }; | 84 | }; |
| 82 | 85 | ||
| 86 | struct NfcState { | ||
| 87 | Common::Input::NfcState state{}; | ||
| 88 | std::vector<u8> data{}; | ||
| 89 | }; | ||
| 90 | |||
| 83 | struct ControllerMotion { | 91 | struct ControllerMotion { |
| 84 | Common::Vec3f accel{}; | 92 | Common::Vec3f accel{}; |
| 85 | Common::Vec3f gyro{}; | 93 | Common::Vec3f gyro{}; |
| @@ -107,6 +115,7 @@ struct ControllerStatus { | |||
| 107 | BatteryValues battery_values{}; | 115 | BatteryValues battery_values{}; |
| 108 | VibrationValues vibration_values{}; | 116 | VibrationValues vibration_values{}; |
| 109 | CameraValues camera_values{}; | 117 | CameraValues camera_values{}; |
| 118 | NfcValues nfc_values{}; | ||
| 110 | 119 | ||
| 111 | // Data for HID serices | 120 | // Data for HID serices |
| 112 | HomeButtonState home_button_state{}; | 121 | HomeButtonState home_button_state{}; |
| @@ -119,6 +128,7 @@ struct ControllerStatus { | |||
| 119 | ControllerColors colors_state{}; | 128 | ControllerColors colors_state{}; |
| 120 | BatteryLevelState battery_state{}; | 129 | BatteryLevelState battery_state{}; |
| 121 | CameraState camera_state{}; | 130 | CameraState camera_state{}; |
| 131 | NfcState nfc_state{}; | ||
| 122 | }; | 132 | }; |
| 123 | 133 | ||
| 124 | enum class ControllerTriggerType { | 134 | enum class ControllerTriggerType { |
| @@ -130,6 +140,7 @@ enum class ControllerTriggerType { | |||
| 130 | Battery, | 140 | Battery, |
| 131 | Vibration, | 141 | Vibration, |
| 132 | IrSensor, | 142 | IrSensor, |
| 143 | Nfc, | ||
| 133 | Connected, | 144 | Connected, |
| 134 | Disconnected, | 145 | Disconnected, |
| 135 | Type, | 146 | Type, |
| @@ -315,6 +326,9 @@ public: | |||
| 315 | /// Returns the latest camera status from the controller | 326 | /// Returns the latest camera status from the controller |
| 316 | const CameraState& GetCamera() const; | 327 | const CameraState& GetCamera() const; |
| 317 | 328 | ||
| 329 | /// Returns the latest ntag status from the controller | ||
| 330 | const NfcState& GetNfc() const; | ||
| 331 | |||
| 318 | /** | 332 | /** |
| 319 | * Sends a specific vibration to the output device | 333 | * Sends a specific vibration to the output device |
| 320 | * @return true if vibration had no errors | 334 | * @return true if vibration had no errors |
| @@ -341,6 +355,12 @@ public: | |||
| 341 | */ | 355 | */ |
| 342 | bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); | 356 | bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); |
| 343 | 357 | ||
| 358 | /// Returns true if the device has nfc support | ||
| 359 | bool HasNfc() const; | ||
| 360 | |||
| 361 | /// Returns true if the nfc tag was written | ||
| 362 | bool WriteNfc(const std::vector<u8>& data); | ||
| 363 | |||
| 344 | /// Returns the led pattern corresponding to this emulated controller | 364 | /// Returns the led pattern corresponding to this emulated controller |
| 345 | LedPattern GetLedPattern() const; | 365 | LedPattern GetLedPattern() const; |
| 346 | 366 | ||
| @@ -425,6 +445,12 @@ private: | |||
| 425 | void SetCamera(const Common::Input::CallbackStatus& callback); | 445 | void SetCamera(const Common::Input::CallbackStatus& callback); |
| 426 | 446 | ||
| 427 | /** | 447 | /** |
| 448 | * Updates the nfc status of the controller | ||
| 449 | * @param callback A CallbackStatus containing the nfc status | ||
| 450 | */ | ||
| 451 | void SetNfc(const Common::Input::CallbackStatus& callback); | ||
| 452 | |||
| 453 | /** | ||
| 428 | * Converts a color format from bgra to rgba | 454 | * Converts a color format from bgra to rgba |
| 429 | * @param color in bgra format | 455 | * @param color in bgra format |
| 430 | * @return NpadColor in rgba format | 456 | * @return NpadColor in rgba format |
| @@ -458,6 +484,7 @@ private: | |||
| 458 | TriggerParams trigger_params; | 484 | TriggerParams trigger_params; |
| 459 | BatteryParams battery_params; | 485 | BatteryParams battery_params; |
| 460 | CameraParams camera_params; | 486 | CameraParams camera_params; |
| 487 | NfcParams nfc_params; | ||
| 461 | OutputParams output_params; | 488 | OutputParams output_params; |
| 462 | 489 | ||
| 463 | ButtonDevices button_devices; | 490 | ButtonDevices button_devices; |
| @@ -466,6 +493,7 @@ private: | |||
| 466 | TriggerDevices trigger_devices; | 493 | TriggerDevices trigger_devices; |
| 467 | BatteryDevices battery_devices; | 494 | BatteryDevices battery_devices; |
| 468 | CameraDevices camera_devices; | 495 | CameraDevices camera_devices; |
| 496 | NfcDevices nfc_devices; | ||
| 469 | OutputDevices output_devices; | 497 | OutputDevices output_devices; |
| 470 | 498 | ||
| 471 | // TAS related variables | 499 | // TAS related variables |
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp index 52fb69e9c..fe9915abe 100644 --- a/src/core/hid/input_converter.cpp +++ b/src/core/hid/input_converter.cpp | |||
| @@ -287,6 +287,20 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu | |||
| 287 | return camera; | 287 | return camera; |
| 288 | } | 288 | } |
| 289 | 289 | ||
| 290 | Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) { | ||
| 291 | Common::Input::NfcStatus nfc{}; | ||
| 292 | switch (callback.type) { | ||
| 293 | case Common::Input::InputType::Nfc: | ||
| 294 | return callback.nfc_status; | ||
| 295 | break; | ||
| 296 | default: | ||
| 297 | LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type); | ||
| 298 | break; | ||
| 299 | } | ||
| 300 | |||
| 301 | return nfc; | ||
| 302 | } | ||
| 303 | |||
| 290 | void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { | 304 | void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { |
| 291 | const auto& properties = analog.properties; | 305 | const auto& properties = analog.properties; |
| 292 | float& raw_value = analog.raw_value; | 306 | float& raw_value = analog.raw_value; |
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h index 143c50cc0..b7eb6e660 100644 --- a/src/core/hid/input_converter.h +++ b/src/core/hid/input_converter.h | |||
| @@ -85,6 +85,14 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu | |||
| 85 | Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); | 85 | Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); |
| 86 | 86 | ||
| 87 | /** | 87 | /** |
| 88 | * Converts raw input data into a valid nfc status. | ||
| 89 | * | ||
| 90 | * @param callback Supported callbacks: Nfc. | ||
| 91 | * @return A valid CameraObject object. | ||
| 92 | */ | ||
| 93 | Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); | ||
| 94 | |||
| 95 | /** | ||
| 88 | * Converts raw analog data into a valid analog value | 96 | * Converts raw analog data into a valid analog value |
| 89 | * @param analog An analog object containing raw data and properties | 97 | * @param analog An analog object containing raw data and properties |
| 90 | * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. | 98 | * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. |
diff --git a/src/core/hle/kernel/k_worker_task_manager.cpp b/src/core/hle/kernel/k_worker_task_manager.cpp index 221f341ee..04042bf8f 100644 --- a/src/core/hle/kernel/k_worker_task_manager.cpp +++ b/src/core/hle/kernel/k_worker_task_manager.cpp | |||
| @@ -23,7 +23,7 @@ void KWorkerTask::DoWorkerTask() { | |||
| 23 | } | 23 | } |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "yuzu:KWorkerTaskManager") {} | 26 | KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {} |
| 27 | 27 | ||
| 28 | void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { | 28 | void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { |
| 29 | ASSERT(type <= WorkerType::Count); | 29 | ASSERT(type <= WorkerType::Count); |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index ce7fa8275..9251f29ad 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -48,7 +48,7 @@ namespace Kernel { | |||
| 48 | struct KernelCore::Impl { | 48 | struct KernelCore::Impl { |
| 49 | explicit Impl(Core::System& system_, KernelCore& kernel_) | 49 | explicit Impl(Core::System& system_, KernelCore& kernel_) |
| 50 | : time_manager{system_}, | 50 | : time_manager{system_}, |
| 51 | service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {} | 51 | service_threads_manager{1, "ServiceThreadsManager"}, system{system_} {} |
| 52 | 52 | ||
| 53 | void SetMulticore(bool is_multi) { | 53 | void SetMulticore(bool is_multi) { |
| 54 | is_multicore = is_multi; | 54 | is_multicore = is_multi; |
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 2e87b4ea4..d23d76706 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp | |||
| @@ -36,7 +36,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std | |||
| 36 | : service_name{name} { | 36 | : service_name{name} { |
| 37 | for (std::size_t i = 0; i < num_threads; ++i) { | 37 | for (std::size_t i = 0; i < num_threads; ++i) { |
| 38 | threads.emplace_back([this, &kernel](std::stop_token stop_token) { | 38 | threads.emplace_back([this, &kernel](std::stop_token stop_token) { |
| 39 | Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str()); | 39 | Common::SetCurrentThreadName(std::string{service_name}.c_str()); |
| 40 | 40 | ||
| 41 | // Wait for first request before trying to acquire a render context | 41 | // Wait for first request before trying to acquire a render context |
| 42 | { | 42 | { |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index cb29004e8..f8972ec7a 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -660,7 +660,6 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing | |||
| 660 | ASSERT(false); | 660 | ASSERT(false); |
| 661 | break; | 661 | break; |
| 662 | case Core::HID::NpadStyleIndex::ProController: | 662 | case Core::HID::NpadStyleIndex::ProController: |
| 663 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 664 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | 663 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); |
| 665 | break; | 664 | break; |
| 666 | case Core::HID::NpadStyleIndex::Handheld: | 665 | case Core::HID::NpadStyleIndex::Handheld: |
| @@ -676,6 +675,11 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing | |||
| 676 | case Core::HID::NpadStyleIndex::JoyconRight: | 675 | case Core::HID::NpadStyleIndex::JoyconRight: |
| 677 | set_motion_state(sixaxis_right_lifo_state, motion_state[1]); | 676 | set_motion_state(sixaxis_right_lifo_state, motion_state[1]); |
| 678 | break; | 677 | break; |
| 678 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 679 | using namespace std::literals::chrono_literals; | ||
| 680 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 681 | sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); | ||
| 682 | break; | ||
| 679 | default: | 683 | default: |
| 680 | break; | 684 | break; |
| 681 | } | 685 | } |
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp new file mode 100644 index 000000000..575d4e626 --- /dev/null +++ b/src/core/hle/service/hid/controllers/palma.cpp | |||
| @@ -0,0 +1,229 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core_timing.h" | ||
| 5 | #include "core/hid/emulated_controller.h" | ||
| 6 | #include "core/hid/hid_core.h" | ||
| 7 | #include "core/hid/hid_types.h" | ||
| 8 | #include "core/hle/kernel/k_event.h" | ||
| 9 | #include "core/hle/kernel/k_readable_event.h" | ||
| 10 | #include "core/hle/service/hid/controllers/palma.h" | ||
| 11 | #include "core/hle/service/kernel_helpers.h" | ||
| 12 | |||
| 13 | namespace Service::HID { | ||
| 14 | |||
| 15 | Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, | ||
| 16 | KernelHelpers::ServiceContext& service_context_) | ||
| 17 | : ControllerBase{hid_core_}, service_context{service_context_} { | ||
| 18 | controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); | ||
| 19 | operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); | ||
| 20 | } | ||
| 21 | |||
| 22 | Controller_Palma::~Controller_Palma() = default; | ||
| 23 | |||
| 24 | void Controller_Palma::OnInit() {} | ||
| 25 | |||
| 26 | void Controller_Palma::OnRelease() {} | ||
| 27 | |||
| 28 | void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 29 | if (!IsControllerActivated()) { | ||
| 30 | return; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, | ||
| 35 | PalmaConnectionHandle& handle) { | ||
| 36 | active_handle.npad_id = npad_id; | ||
| 37 | handle = active_handle; | ||
| 38 | return ResultSuccess; | ||
| 39 | } | ||
| 40 | |||
| 41 | Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) { | ||
| 42 | if (handle.npad_id != active_handle.npad_id) { | ||
| 43 | return InvalidPalmaHandle; | ||
| 44 | } | ||
| 45 | ActivateController(); | ||
| 46 | return ResultSuccess; | ||
| 47 | } | ||
| 48 | |||
| 49 | Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent( | ||
| 50 | const PalmaConnectionHandle& handle) const { | ||
| 51 | if (handle.npad_id != active_handle.npad_id) { | ||
| 52 | LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id); | ||
| 53 | } | ||
| 54 | return operation_complete_event->GetReadableEvent(); | ||
| 55 | } | ||
| 56 | |||
| 57 | Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle, | ||
| 58 | PalmaOperationType& operation_type, | ||
| 59 | PalmaOperationData& data) const { | ||
| 60 | if (handle.npad_id != active_handle.npad_id) { | ||
| 61 | return InvalidPalmaHandle; | ||
| 62 | } | ||
| 63 | operation_type = operation.operation; | ||
| 64 | data = operation.data; | ||
| 65 | return ResultSuccess; | ||
| 66 | } | ||
| 67 | |||
| 68 | Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle, | ||
| 69 | u64 palma_activity) { | ||
| 70 | if (handle.npad_id != active_handle.npad_id) { | ||
| 71 | return InvalidPalmaHandle; | ||
| 72 | } | ||
| 73 | operation.operation = PalmaOperationType::PlayActivity; | ||
| 74 | operation.result = PalmaResultSuccess; | ||
| 75 | operation.data = {}; | ||
| 76 | operation_complete_event->GetWritableEvent().Signal(); | ||
| 77 | return ResultSuccess; | ||
| 78 | } | ||
| 79 | |||
| 80 | Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle, | ||
| 81 | PalmaFrModeType fr_mode_) { | ||
| 82 | if (handle.npad_id != active_handle.npad_id) { | ||
| 83 | return InvalidPalmaHandle; | ||
| 84 | } | ||
| 85 | fr_mode = fr_mode_; | ||
| 86 | return ResultSuccess; | ||
| 87 | } | ||
| 88 | |||
| 89 | Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) { | ||
| 90 | if (handle.npad_id != active_handle.npad_id) { | ||
| 91 | return InvalidPalmaHandle; | ||
| 92 | } | ||
| 93 | operation.operation = PalmaOperationType::ReadStep; | ||
| 94 | operation.result = PalmaResultSuccess; | ||
| 95 | operation.data = {}; | ||
| 96 | operation_complete_event->GetWritableEvent().Signal(); | ||
| 97 | return ResultSuccess; | ||
| 98 | } | ||
| 99 | |||
| 100 | Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) { | ||
| 101 | if (handle.npad_id != active_handle.npad_id) { | ||
| 102 | return InvalidPalmaHandle; | ||
| 103 | } | ||
| 104 | return ResultSuccess; | ||
| 105 | } | ||
| 106 | |||
| 107 | Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) { | ||
| 108 | if (handle.npad_id != active_handle.npad_id) { | ||
| 109 | return InvalidPalmaHandle; | ||
| 110 | } | ||
| 111 | return ResultSuccess; | ||
| 112 | } | ||
| 113 | |||
| 114 | void Controller_Palma::ReadPalmaApplicationSection() {} | ||
| 115 | |||
| 116 | void Controller_Palma::WritePalmaApplicationSection() {} | ||
| 117 | |||
| 118 | Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) { | ||
| 119 | if (handle.npad_id != active_handle.npad_id) { | ||
| 120 | return InvalidPalmaHandle; | ||
| 121 | } | ||
| 122 | operation.operation = PalmaOperationType::ReadUniqueCode; | ||
| 123 | operation.result = PalmaResultSuccess; | ||
| 124 | operation.data = {}; | ||
| 125 | operation_complete_event->GetWritableEvent().Signal(); | ||
| 126 | return ResultSuccess; | ||
| 127 | } | ||
| 128 | |||
| 129 | Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) { | ||
| 130 | if (handle.npad_id != active_handle.npad_id) { | ||
| 131 | return InvalidPalmaHandle; | ||
| 132 | } | ||
| 133 | operation.operation = PalmaOperationType::SetUniqueCodeInvalid; | ||
| 134 | operation.result = PalmaResultSuccess; | ||
| 135 | operation.data = {}; | ||
| 136 | operation_complete_event->GetWritableEvent().Signal(); | ||
| 137 | return ResultSuccess; | ||
| 138 | } | ||
| 139 | |||
| 140 | void Controller_Palma::WritePalmaActivityEntry() {} | ||
| 141 | |||
| 142 | Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, | ||
| 143 | u64 unknown) { | ||
| 144 | if (handle.npad_id != active_handle.npad_id) { | ||
| 145 | return InvalidPalmaHandle; | ||
| 146 | } | ||
| 147 | operation.operation = PalmaOperationType::WriteRgbLedPatternEntry; | ||
| 148 | operation.result = PalmaResultSuccess; | ||
| 149 | operation.data = {}; | ||
| 150 | operation_complete_event->GetWritableEvent().Signal(); | ||
| 151 | return ResultSuccess; | ||
| 152 | } | ||
| 153 | |||
| 154 | Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, | ||
| 155 | u8* t_mem, u64 size) { | ||
| 156 | if (handle.npad_id != active_handle.npad_id) { | ||
| 157 | return InvalidPalmaHandle; | ||
| 158 | } | ||
| 159 | operation.operation = PalmaOperationType::WriteWaveEntry; | ||
| 160 | operation.result = PalmaResultSuccess; | ||
| 161 | operation.data = {}; | ||
| 162 | operation_complete_event->GetWritableEvent().Signal(); | ||
| 163 | return ResultSuccess; | ||
| 164 | } | ||
| 165 | |||
| 166 | Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, | ||
| 167 | s32 database_id_version_) { | ||
| 168 | if (handle.npad_id != active_handle.npad_id) { | ||
| 169 | return InvalidPalmaHandle; | ||
| 170 | } | ||
| 171 | database_id_version = database_id_version_; | ||
| 172 | operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; | ||
| 173 | operation.result = PalmaResultSuccess; | ||
| 174 | operation.data[0] = {}; | ||
| 175 | operation_complete_event->GetWritableEvent().Signal(); | ||
| 176 | return ResultSuccess; | ||
| 177 | } | ||
| 178 | |||
| 179 | Result Controller_Palma::GetPalmaDataBaseIdentificationVersion( | ||
| 180 | const PalmaConnectionHandle& handle) { | ||
| 181 | if (handle.npad_id != active_handle.npad_id) { | ||
| 182 | return InvalidPalmaHandle; | ||
| 183 | } | ||
| 184 | operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion; | ||
| 185 | operation.result = PalmaResultSuccess; | ||
| 186 | operation.data = {}; | ||
| 187 | operation.data[0] = static_cast<u8>(database_id_version); | ||
| 188 | operation_complete_event->GetWritableEvent().Signal(); | ||
| 189 | return ResultSuccess; | ||
| 190 | } | ||
| 191 | |||
| 192 | void Controller_Palma::SuspendPalmaFeature() {} | ||
| 193 | |||
| 194 | Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const { | ||
| 195 | if (handle.npad_id != active_handle.npad_id) { | ||
| 196 | return InvalidPalmaHandle; | ||
| 197 | } | ||
| 198 | return operation.result; | ||
| 199 | } | ||
| 200 | void Controller_Palma::ReadPalmaPlayLog() {} | ||
| 201 | |||
| 202 | void Controller_Palma::ResetPalmaPlayLog() {} | ||
| 203 | |||
| 204 | void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) { | ||
| 205 | // If true controllers are able to be paired | ||
| 206 | is_connectable = is_all_connectable; | ||
| 207 | } | ||
| 208 | |||
| 209 | void Controller_Palma::SetIsPalmaPairedConnectable() {} | ||
| 210 | |||
| 211 | Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) { | ||
| 212 | if (handle.npad_id != active_handle.npad_id) { | ||
| 213 | return InvalidPalmaHandle; | ||
| 214 | } | ||
| 215 | // TODO: Do something | ||
| 216 | return ResultSuccess; | ||
| 217 | } | ||
| 218 | |||
| 219 | void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {} | ||
| 220 | |||
| 221 | void Controller_Palma::CancelWritePalmaWaveEntry() {} | ||
| 222 | |||
| 223 | void Controller_Palma::EnablePalmaBoostMode() {} | ||
| 224 | |||
| 225 | void Controller_Palma::GetPalmaBluetoothAddress() {} | ||
| 226 | |||
| 227 | void Controller_Palma::SetDisallowedPalmaConnection() {} | ||
| 228 | |||
| 229 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h new file mode 100644 index 000000000..1d7fc94e1 --- /dev/null +++ b/src/core/hle/service/hid/controllers/palma.h | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include "common/common_funcs.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/hle/service/hid/controllers/controller_base.h" | ||
| 10 | #include "core/hle/service/hid/errors.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | class KEvent; | ||
| 14 | class KReadableEvent; | ||
| 15 | } // namespace Kernel | ||
| 16 | |||
| 17 | namespace Service::KernelHelpers { | ||
| 18 | class ServiceContext; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace Core::HID { | ||
| 22 | class EmulatedController; | ||
| 23 | } // namespace Core::HID | ||
| 24 | |||
| 25 | namespace Service::HID { | ||
| 26 | class Controller_Palma final : public ControllerBase { | ||
| 27 | public: | ||
| 28 | using PalmaOperationData = std::array<u8, 0x140>; | ||
| 29 | |||
| 30 | // This is nn::hid::PalmaOperationType | ||
| 31 | enum class PalmaOperationType { | ||
| 32 | PlayActivity, | ||
| 33 | SetFrModeType, | ||
| 34 | ReadStep, | ||
| 35 | EnableStep, | ||
| 36 | ResetStep, | ||
| 37 | ReadApplicationSection, | ||
| 38 | WriteApplicationSection, | ||
| 39 | ReadUniqueCode, | ||
| 40 | SetUniqueCodeInvalid, | ||
| 41 | WriteActivityEntry, | ||
| 42 | WriteRgbLedPatternEntry, | ||
| 43 | WriteWaveEntry, | ||
| 44 | ReadDataBaseIdentificationVersion, | ||
| 45 | WriteDataBaseIdentificationVersion, | ||
| 46 | SuspendFeature, | ||
| 47 | ReadPlayLog, | ||
| 48 | ResetPlayLog, | ||
| 49 | }; | ||
| 50 | |||
| 51 | // This is nn::hid::PalmaWaveSet | ||
| 52 | enum class PalmaWaveSet : u64 { | ||
| 53 | Small, | ||
| 54 | Medium, | ||
| 55 | Large, | ||
| 56 | }; | ||
| 57 | |||
| 58 | // This is nn::hid::PalmaFrModeType | ||
| 59 | enum class PalmaFrModeType : u64 { | ||
| 60 | Off, | ||
| 61 | B01, | ||
| 62 | B02, | ||
| 63 | B03, | ||
| 64 | Downloaded, | ||
| 65 | }; | ||
| 66 | |||
| 67 | // This is nn::hid::PalmaFeature | ||
| 68 | enum class PalmaFeature : u64 { | ||
| 69 | FrMode, | ||
| 70 | RumbleFeedback, | ||
| 71 | Step, | ||
| 72 | MuteSwitch, | ||
| 73 | }; | ||
| 74 | |||
| 75 | // This is nn::hid::PalmaOperationInfo | ||
| 76 | struct PalmaOperationInfo { | ||
| 77 | PalmaOperationType operation{}; | ||
| 78 | Result result{PalmaResultSuccess}; | ||
| 79 | PalmaOperationData data{}; | ||
| 80 | }; | ||
| 81 | static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size"); | ||
| 82 | |||
| 83 | // This is nn::hid::PalmaActivityEntry | ||
| 84 | struct PalmaActivityEntry { | ||
| 85 | u32 rgb_led_pattern_index; | ||
| 86 | INSERT_PADDING_BYTES(2); | ||
| 87 | PalmaWaveSet wave_set; | ||
| 88 | u32 wave_index; | ||
| 89 | INSERT_PADDING_BYTES(12); | ||
| 90 | }; | ||
| 91 | static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size"); | ||
| 92 | |||
| 93 | struct PalmaConnectionHandle { | ||
| 94 | Core::HID::NpadIdType npad_id; | ||
| 95 | INSERT_PADDING_BYTES(4); // Unknown | ||
| 96 | }; | ||
| 97 | static_assert(sizeof(PalmaConnectionHandle) == 0x8, | ||
| 98 | "PalmaConnectionHandle has incorrect size."); | ||
| 99 | |||
| 100 | explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, | ||
| 101 | KernelHelpers::ServiceContext& service_context_); | ||
| 102 | ~Controller_Palma() override; | ||
| 103 | |||
| 104 | // Called when the controller is initialized | ||
| 105 | void OnInit() override; | ||
| 106 | |||
| 107 | // When the controller is released | ||
| 108 | void OnRelease() override; | ||
| 109 | |||
| 110 | // When the controller is requesting an update for the shared memory | ||
| 111 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 112 | |||
| 113 | Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle); | ||
| 114 | Result InitializePalma(const PalmaConnectionHandle& handle); | ||
| 115 | Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent( | ||
| 116 | const PalmaConnectionHandle& handle) const; | ||
| 117 | Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle, | ||
| 118 | PalmaOperationType& operation_type, | ||
| 119 | PalmaOperationData& data) const; | ||
| 120 | Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity); | ||
| 121 | Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_); | ||
| 122 | Result ReadPalmaStep(const PalmaConnectionHandle& handle); | ||
| 123 | Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled); | ||
| 124 | Result ResetPalmaStep(const PalmaConnectionHandle& handle); | ||
| 125 | Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle); | ||
| 126 | Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle); | ||
| 127 | Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown); | ||
| 128 | Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, u8* t_mem, | ||
| 129 | u64 size); | ||
| 130 | Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle, | ||
| 131 | s32 database_id_version_); | ||
| 132 | Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle); | ||
| 133 | Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const; | ||
| 134 | void SetIsPalmaAllConnectable(bool is_all_connectable); | ||
| 135 | Result PairPalma(const PalmaConnectionHandle& handle); | ||
| 136 | void SetPalmaBoostMode(bool boost_mode); | ||
| 137 | |||
| 138 | private: | ||
| 139 | void ReadPalmaApplicationSection(); | ||
| 140 | void WritePalmaApplicationSection(); | ||
| 141 | void WritePalmaActivityEntry(); | ||
| 142 | void SuspendPalmaFeature(); | ||
| 143 | void ReadPalmaPlayLog(); | ||
| 144 | void ResetPalmaPlayLog(); | ||
| 145 | void SetIsPalmaPairedConnectable(); | ||
| 146 | void CancelWritePalmaWaveEntry(); | ||
| 147 | void EnablePalmaBoostMode(); | ||
| 148 | void GetPalmaBluetoothAddress(); | ||
| 149 | void SetDisallowedPalmaConnection(); | ||
| 150 | |||
| 151 | bool is_connectable{}; | ||
| 152 | s32 database_id_version{}; | ||
| 153 | PalmaOperationInfo operation{}; | ||
| 154 | PalmaFrModeType fr_mode{}; | ||
| 155 | PalmaConnectionHandle active_handle{}; | ||
| 156 | |||
| 157 | Core::HID::EmulatedController* controller; | ||
| 158 | |||
| 159 | Kernel::KEvent* operation_complete_event; | ||
| 160 | KernelHelpers::ServiceContext& service_context; | ||
| 161 | }; | ||
| 162 | |||
| 163 | } // namespace Service::HID | ||
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h index 4613a4e60..76208e9a4 100644 --- a/src/core/hle/service/hid/errors.h +++ b/src/core/hle/service/hid/errors.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | namespace Service::HID { | 8 | namespace Service::HID { |
| 9 | 9 | ||
| 10 | constexpr Result PalmaResultSuccess{ErrorModule::HID, 0}; | ||
| 10 | constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; | 11 | constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; |
| 11 | constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; | 12 | constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; |
| 12 | constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; | 13 | constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; |
| @@ -17,6 +18,7 @@ constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; | |||
| 17 | constexpr Result NpadIsSameType{ErrorModule::HID, 602}; | 18 | constexpr Result NpadIsSameType{ErrorModule::HID, 602}; |
| 18 | constexpr Result InvalidNpadId{ErrorModule::HID, 709}; | 19 | constexpr Result InvalidNpadId{ErrorModule::HID, 709}; |
| 19 | constexpr Result NpadNotConnected{ErrorModule::HID, 710}; | 20 | constexpr Result NpadNotConnected{ErrorModule::HID, 710}; |
| 21 | constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302}; | ||
| 20 | 22 | ||
| 21 | } // namespace Service::HID | 23 | } // namespace Service::HID |
| 22 | 24 | ||
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 3d3457160..46bad7871 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include "core/hle/service/hid/controllers/keyboard.h" | 27 | #include "core/hle/service/hid/controllers/keyboard.h" |
| 28 | #include "core/hle/service/hid/controllers/mouse.h" | 28 | #include "core/hle/service/hid/controllers/mouse.h" |
| 29 | #include "core/hle/service/hid/controllers/npad.h" | 29 | #include "core/hle/service/hid/controllers/npad.h" |
| 30 | #include "core/hle/service/hid/controllers/palma.h" | ||
| 30 | #include "core/hle/service/hid/controllers/stubbed.h" | 31 | #include "core/hle/service/hid/controllers/stubbed.h" |
| 31 | #include "core/hle/service/hid/controllers/touchscreen.h" | 32 | #include "core/hle/service/hid/controllers/touchscreen.h" |
| 32 | #include "core/hle/service/hid/controllers/xpad.h" | 33 | #include "core/hle/service/hid/controllers/xpad.h" |
| @@ -35,7 +36,8 @@ namespace Service::HID { | |||
| 35 | 36 | ||
| 36 | // Updating period for each HID device. | 37 | // Updating period for each HID device. |
| 37 | // Period time is obtained by measuring the number of samples in a second on HW using a homebrew | 38 | // Period time is obtained by measuring the number of samples in a second on HW using a homebrew |
| 38 | constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz) | 39 | // Correct pad_update_ns is 4ms this is overclocked to lower input lag |
| 40 | constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) | ||
| 39 | constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) | 41 | constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) |
| 40 | constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) | 42 | constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) |
| 41 | 43 | ||
| @@ -60,6 +62,7 @@ IAppletResource::IAppletResource(Core::System& system_, | |||
| 60 | MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory); | 62 | MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory); |
| 61 | MakeController<Controller_Gesture>(HidController::Gesture, shared_memory); | 63 | MakeController<Controller_Gesture>(HidController::Gesture, shared_memory); |
| 62 | MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory); | 64 | MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory); |
| 65 | MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory); | ||
| 63 | 66 | ||
| 64 | // Homebrew doesn't try to activate some controllers, so we activate them by default | 67 | // Homebrew doesn't try to activate some controllers, so we activate them by default |
| 65 | GetController<Controller_NPad>(HidController::NPad).ActivateController(); | 68 | GetController<Controller_NPad>(HidController::NPad).ActivateController(); |
| @@ -310,36 +313,36 @@ Hid::Hid(Core::System& system_) | |||
| 310 | {406, nullptr, "GetNpadLeftRightInterfaceType"}, | 313 | {406, nullptr, "GetNpadLeftRightInterfaceType"}, |
| 311 | {407, nullptr, "GetNpadOfHighestBatteryLevel"}, | 314 | {407, nullptr, "GetNpadOfHighestBatteryLevel"}, |
| 312 | {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, | 315 | {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, |
| 313 | {500, nullptr, "GetPalmaConnectionHandle"}, | 316 | {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"}, |
| 314 | {501, nullptr, "InitializePalma"}, | 317 | {501, &Hid::InitializePalma, "InitializePalma"}, |
| 315 | {502, nullptr, "AcquirePalmaOperationCompleteEvent"}, | 318 | {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"}, |
| 316 | {503, nullptr, "GetPalmaOperationInfo"}, | 319 | {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"}, |
| 317 | {504, nullptr, "PlayPalmaActivity"}, | 320 | {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"}, |
| 318 | {505, nullptr, "SetPalmaFrModeType"}, | 321 | {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"}, |
| 319 | {506, nullptr, "ReadPalmaStep"}, | 322 | {506, &Hid::ReadPalmaStep, "ReadPalmaStep"}, |
| 320 | {507, nullptr, "EnablePalmaStep"}, | 323 | {507, &Hid::EnablePalmaStep, "EnablePalmaStep"}, |
| 321 | {508, nullptr, "ResetPalmaStep"}, | 324 | {508, &Hid::ResetPalmaStep, "ResetPalmaStep"}, |
| 322 | {509, nullptr, "ReadPalmaApplicationSection"}, | 325 | {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"}, |
| 323 | {510, nullptr, "WritePalmaApplicationSection"}, | 326 | {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"}, |
| 324 | {511, nullptr, "ReadPalmaUniqueCode"}, | 327 | {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"}, |
| 325 | {512, nullptr, "SetPalmaUniqueCodeInvalid"}, | 328 | {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"}, |
| 326 | {513, nullptr, "WritePalmaActivityEntry"}, | 329 | {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"}, |
| 327 | {514, nullptr, "WritePalmaRgbLedPatternEntry"}, | 330 | {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"}, |
| 328 | {515, nullptr, "WritePalmaWaveEntry"}, | 331 | {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"}, |
| 329 | {516, nullptr, "SetPalmaDataBaseIdentificationVersion"}, | 332 | {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"}, |
| 330 | {517, nullptr, "GetPalmaDataBaseIdentificationVersion"}, | 333 | {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"}, |
| 331 | {518, nullptr, "SuspendPalmaFeature"}, | 334 | {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"}, |
| 332 | {519, nullptr, "GetPalmaOperationResult"}, | 335 | {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"}, |
| 333 | {520, nullptr, "ReadPalmaPlayLog"}, | 336 | {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"}, |
| 334 | {521, nullptr, "ResetPalmaPlayLog"}, | 337 | {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"}, |
| 335 | {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, | 338 | {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, |
| 336 | {523, nullptr, "SetIsPalmaPairedConnectable"}, | 339 | {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"}, |
| 337 | {524, nullptr, "PairPalma"}, | 340 | {524, &Hid::PairPalma, "PairPalma"}, |
| 338 | {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, | 341 | {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, |
| 339 | {526, nullptr, "CancelWritePalmaWaveEntry"}, | 342 | {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"}, |
| 340 | {527, nullptr, "EnablePalmaBoostMode"}, | 343 | {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"}, |
| 341 | {528, nullptr, "GetPalmaBluetoothAddress"}, | 344 | {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"}, |
| 342 | {529, nullptr, "SetDisallowedPalmaConnection"}, | 345 | {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"}, |
| 343 | {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, | 346 | {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, |
| 344 | {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, | 347 | {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, |
| 345 | {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, | 348 | {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, |
| @@ -1878,14 +1881,361 @@ void Hid::IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx) { | |||
| 1878 | rb.Push(false); | 1881 | rb.Push(false); |
| 1879 | } | 1882 | } |
| 1880 | 1883 | ||
| 1884 | void Hid::GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx) { | ||
| 1885 | IPC::RequestParser rp{ctx}; | ||
| 1886 | struct Parameters { | ||
| 1887 | Core::HID::NpadIdType npad_id; | ||
| 1888 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 1889 | u64 applet_resource_user_id; | ||
| 1890 | }; | ||
| 1891 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 1892 | |||
| 1893 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1894 | |||
| 1895 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", | ||
| 1896 | parameters.npad_id, parameters.applet_resource_user_id); | ||
| 1897 | |||
| 1898 | Controller_Palma::PalmaConnectionHandle handle; | ||
| 1899 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 1900 | const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle); | ||
| 1901 | |||
| 1902 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1903 | rb.Push(result); | ||
| 1904 | rb.PushRaw(handle); | ||
| 1905 | } | ||
| 1906 | |||
| 1907 | void Hid::InitializePalma(Kernel::HLERequestContext& ctx) { | ||
| 1908 | IPC::RequestParser rp{ctx}; | ||
| 1909 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 1910 | |||
| 1911 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 1912 | |||
| 1913 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 1914 | const auto result = controller.InitializePalma(connection_handle); | ||
| 1915 | |||
| 1916 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1917 | rb.Push(result); | ||
| 1918 | } | ||
| 1919 | |||
| 1920 | void Hid::AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx) { | ||
| 1921 | IPC::RequestParser rp{ctx}; | ||
| 1922 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 1923 | |||
| 1924 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 1925 | |||
| 1926 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 1927 | |||
| 1928 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 1929 | rb.Push(ResultSuccess); | ||
| 1930 | rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle)); | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | void Hid::GetPalmaOperationInfo(Kernel::HLERequestContext& ctx) { | ||
| 1934 | IPC::RequestParser rp{ctx}; | ||
| 1935 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 1936 | |||
| 1937 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 1938 | |||
| 1939 | Controller_Palma::PalmaOperationType operation_type; | ||
| 1940 | Controller_Palma::PalmaOperationData data; | ||
| 1941 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 1942 | const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data); | ||
| 1943 | |||
| 1944 | if (result.IsError()) { | ||
| 1945 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1946 | rb.Push(result); | ||
| 1947 | } | ||
| 1948 | |||
| 1949 | ctx.WriteBuffer(data); | ||
| 1950 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1951 | rb.Push(result); | ||
| 1952 | rb.Push(static_cast<u64>(operation_type)); | ||
| 1953 | } | ||
| 1954 | |||
| 1955 | void Hid::PlayPalmaActivity(Kernel::HLERequestContext& ctx) { | ||
| 1956 | IPC::RequestParser rp{ctx}; | ||
| 1957 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 1958 | const auto palma_activity{rp.Pop<u64>()}; | ||
| 1959 | |||
| 1960 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", | ||
| 1961 | connection_handle.npad_id, palma_activity); | ||
| 1962 | |||
| 1963 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 1964 | const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity); | ||
| 1965 | |||
| 1966 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1967 | rb.Push(result); | ||
| 1968 | } | ||
| 1969 | |||
| 1970 | void Hid::SetPalmaFrModeType(Kernel::HLERequestContext& ctx) { | ||
| 1971 | IPC::RequestParser rp{ctx}; | ||
| 1972 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 1973 | const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()}; | ||
| 1974 | |||
| 1975 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", | ||
| 1976 | connection_handle.npad_id, fr_mode); | ||
| 1977 | |||
| 1978 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 1979 | const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode); | ||
| 1980 | |||
| 1981 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1982 | rb.Push(result); | ||
| 1983 | } | ||
| 1984 | |||
| 1985 | void Hid::ReadPalmaStep(Kernel::HLERequestContext& ctx) { | ||
| 1986 | IPC::RequestParser rp{ctx}; | ||
| 1987 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 1988 | |||
| 1989 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 1990 | |||
| 1991 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 1992 | const auto result = controller.ReadPalmaStep(connection_handle); | ||
| 1993 | |||
| 1994 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1995 | rb.Push(result); | ||
| 1996 | } | ||
| 1997 | |||
| 1998 | void Hid::EnablePalmaStep(Kernel::HLERequestContext& ctx) { | ||
| 1999 | IPC::RequestParser rp{ctx}; | ||
| 2000 | struct Parameters { | ||
| 2001 | bool is_enabled; | ||
| 2002 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 2003 | Controller_Palma::PalmaConnectionHandle connection_handle; | ||
| 2004 | }; | ||
| 2005 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 2006 | |||
| 2007 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 2008 | |||
| 2009 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", | ||
| 2010 | parameters.connection_handle.npad_id, parameters.is_enabled); | ||
| 2011 | |||
| 2012 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 2013 | const auto result = | ||
| 2014 | controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); | ||
| 2015 | |||
| 2016 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2017 | rb.Push(result); | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | void Hid::ResetPalmaStep(Kernel::HLERequestContext& ctx) { | ||
| 2021 | IPC::RequestParser rp{ctx}; | ||
| 2022 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2023 | |||
| 2024 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2025 | |||
| 2026 | auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma); | ||
| 2027 | const auto result = controller.ResetPalmaStep(connection_handle); | ||
| 2028 | |||
| 2029 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2030 | rb.Push(result); | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | void Hid::ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx) { | ||
| 2034 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2035 | |||
| 2036 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2037 | rb.Push(ResultSuccess); | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | void Hid::WritePalmaApplicationSection(Kernel::HLERequestContext& ctx) { | ||
| 2041 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2042 | |||
| 2043 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2044 | rb.Push(ResultSuccess); | ||
| 2045 | } | ||
| 2046 | |||
| 2047 | void Hid::ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx) { | ||
| 2048 | IPC::RequestParser rp{ctx}; | ||
| 2049 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2050 | |||
| 2051 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2052 | |||
| 2053 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2054 | .ReadPalmaUniqueCode(connection_handle); | ||
| 2055 | |||
| 2056 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2057 | rb.Push(ResultSuccess); | ||
| 2058 | } | ||
| 2059 | |||
| 2060 | void Hid::SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx) { | ||
| 2061 | IPC::RequestParser rp{ctx}; | ||
| 2062 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2063 | |||
| 2064 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2065 | |||
| 2066 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2067 | .SetPalmaUniqueCodeInvalid(connection_handle); | ||
| 2068 | |||
| 2069 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2070 | rb.Push(ResultSuccess); | ||
| 2071 | } | ||
| 2072 | |||
| 2073 | void Hid::WritePalmaActivityEntry(Kernel::HLERequestContext& ctx) { | ||
| 2074 | LOG_CRITICAL(Service_HID, "(STUBBED) called"); | ||
| 2075 | |||
| 2076 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2077 | rb.Push(ResultSuccess); | ||
| 2078 | } | ||
| 2079 | |||
| 2080 | void Hid::WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx) { | ||
| 2081 | IPC::RequestParser rp{ctx}; | ||
| 2082 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2083 | const auto unknown{rp.Pop<u64>()}; | ||
| 2084 | |||
| 2085 | const auto buffer = ctx.ReadBuffer(); | ||
| 2086 | |||
| 2087 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", | ||
| 2088 | connection_handle.npad_id, unknown); | ||
| 2089 | |||
| 2090 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2091 | .WritePalmaRgbLedPatternEntry(connection_handle, unknown); | ||
| 2092 | |||
| 2093 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2094 | rb.Push(ResultSuccess); | ||
| 2095 | } | ||
| 2096 | |||
| 2097 | void Hid::WritePalmaWaveEntry(Kernel::HLERequestContext& ctx) { | ||
| 2098 | IPC::RequestParser rp{ctx}; | ||
| 2099 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2100 | const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()}; | ||
| 2101 | const auto unknown{rp.Pop<u64>()}; | ||
| 2102 | const auto t_mem_size{rp.Pop<u64>()}; | ||
| 2103 | const auto t_mem_handle{ctx.GetCopyHandle(0)}; | ||
| 2104 | const auto size{rp.Pop<u64>()}; | ||
| 2105 | |||
| 2106 | ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); | ||
| 2107 | |||
| 2108 | auto t_mem = | ||
| 2109 | system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); | ||
| 2110 | |||
| 2111 | if (t_mem.IsNull()) { | ||
| 2112 | LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); | ||
| 2113 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2114 | rb.Push(ResultUnknown); | ||
| 2115 | return; | ||
| 2116 | } | ||
| 2117 | |||
| 2118 | ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); | ||
| 2119 | |||
| 2120 | LOG_WARNING(Service_HID, | ||
| 2121 | "(STUBBED) called, connection_handle={}, wave_set={}, unkown={}, " | ||
| 2122 | "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", | ||
| 2123 | connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); | ||
| 2124 | |||
| 2125 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2126 | .WritePalmaWaveEntry(connection_handle, wave_set, | ||
| 2127 | system.Memory().GetPointer(t_mem->GetSourceAddress()), t_mem_size); | ||
| 2128 | |||
| 2129 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2130 | rb.Push(ResultSuccess); | ||
| 2131 | } | ||
| 2132 | |||
| 2133 | void Hid::SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) { | ||
| 2134 | IPC::RequestParser rp{ctx}; | ||
| 2135 | struct Parameters { | ||
| 2136 | s32 database_id_version; | ||
| 2137 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 2138 | Controller_Palma::PalmaConnectionHandle connection_handle; | ||
| 2139 | }; | ||
| 2140 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 2141 | |||
| 2142 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 2143 | |||
| 2144 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", | ||
| 2145 | parameters.connection_handle.npad_id, parameters.database_id_version); | ||
| 2146 | |||
| 2147 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2148 | .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle, | ||
| 2149 | parameters.database_id_version); | ||
| 2150 | |||
| 2151 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2152 | rb.Push(ResultSuccess); | ||
| 2153 | } | ||
| 2154 | |||
| 2155 | void Hid::GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) { | ||
| 2156 | IPC::RequestParser rp{ctx}; | ||
| 2157 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2158 | |||
| 2159 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2160 | |||
| 2161 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2162 | .GetPalmaDataBaseIdentificationVersion(connection_handle); | ||
| 2163 | |||
| 2164 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2165 | rb.Push(ResultSuccess); | ||
| 2166 | } | ||
| 2167 | |||
| 2168 | void Hid::SuspendPalmaFeature(Kernel::HLERequestContext& ctx) { | ||
| 2169 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2170 | |||
| 2171 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2172 | rb.Push(ResultSuccess); | ||
| 2173 | } | ||
| 2174 | |||
| 2175 | void Hid::GetPalmaOperationResult(Kernel::HLERequestContext& ctx) { | ||
| 2176 | IPC::RequestParser rp{ctx}; | ||
| 2177 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2178 | |||
| 2179 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2180 | |||
| 2181 | const auto result = applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2182 | .GetPalmaOperationResult(connection_handle); | ||
| 2183 | |||
| 2184 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2185 | rb.Push(result); | ||
| 2186 | } | ||
| 2187 | |||
| 2188 | void Hid::ReadPalmaPlayLog(Kernel::HLERequestContext& ctx) { | ||
| 2189 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2190 | |||
| 2191 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2192 | rb.Push(ResultSuccess); | ||
| 2193 | } | ||
| 2194 | |||
| 2195 | void Hid::ResetPalmaPlayLog(Kernel::HLERequestContext& ctx) { | ||
| 2196 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2197 | |||
| 2198 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2199 | rb.Push(ResultSuccess); | ||
| 2200 | } | ||
| 2201 | |||
| 1881 | void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { | 2202 | void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { |
| 1882 | IPC::RequestParser rp{ctx}; | 2203 | IPC::RequestParser rp{ctx}; |
| 1883 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 2204 | struct Parameters { |
| 1884 | const auto is_palma_all_connectable{rp.Pop<bool>()}; | 2205 | bool is_palma_all_connectable; |
| 2206 | INSERT_PADDING_BYTES_NOINIT(7); | ||
| 2207 | u64 applet_resource_user_id; | ||
| 2208 | }; | ||
| 2209 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 2210 | |||
| 2211 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 1885 | 2212 | ||
| 1886 | LOG_WARNING(Service_HID, | 2213 | LOG_WARNING(Service_HID, |
| 1887 | "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}", | 2214 | "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", |
| 1888 | applet_resource_user_id, is_palma_all_connectable); | 2215 | parameters.is_palma_all_connectable, parameters.applet_resource_user_id); |
| 2216 | |||
| 2217 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2218 | .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); | ||
| 2219 | |||
| 2220 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2221 | rb.Push(ResultSuccess); | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | void Hid::SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx) { | ||
| 2225 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2226 | |||
| 2227 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2228 | rb.Push(ResultSuccess); | ||
| 2229 | } | ||
| 2230 | |||
| 2231 | void Hid::PairPalma(Kernel::HLERequestContext& ctx) { | ||
| 2232 | IPC::RequestParser rp{ctx}; | ||
| 2233 | const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; | ||
| 2234 | |||
| 2235 | LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); | ||
| 2236 | |||
| 2237 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2238 | .PairPalma(connection_handle); | ||
| 1889 | 2239 | ||
| 1890 | IPC::ResponseBuilder rb{ctx, 2}; | 2240 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1891 | rb.Push(ResultSuccess); | 2241 | rb.Push(ResultSuccess); |
| @@ -1897,6 +2247,37 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { | |||
| 1897 | 2247 | ||
| 1898 | LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); | 2248 | LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); |
| 1899 | 2249 | ||
| 2250 | applet_resource->GetController<Controller_Palma>(HidController::Palma) | ||
| 2251 | .SetPalmaBoostMode(palma_boost_mode); | ||
| 2252 | |||
| 2253 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2254 | rb.Push(ResultSuccess); | ||
| 2255 | } | ||
| 2256 | |||
| 2257 | void Hid::CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx) { | ||
| 2258 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2259 | |||
| 2260 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2261 | rb.Push(ResultSuccess); | ||
| 2262 | } | ||
| 2263 | |||
| 2264 | void Hid::EnablePalmaBoostMode(Kernel::HLERequestContext& ctx) { | ||
| 2265 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2266 | |||
| 2267 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2268 | rb.Push(ResultSuccess); | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | void Hid::GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx) { | ||
| 2272 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2273 | |||
| 2274 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 2275 | rb.Push(ResultSuccess); | ||
| 2276 | } | ||
| 2277 | |||
| 2278 | void Hid::SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx) { | ||
| 2279 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 2280 | |||
| 1900 | IPC::ResponseBuilder rb{ctx, 2}; | 2281 | IPC::ResponseBuilder rb{ctx, 2}; |
| 1901 | rb.Push(ResultSuccess); | 2282 | rb.Push(ResultSuccess); |
| 1902 | } | 2283 | } |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index ac4333022..340d26fdc 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -33,6 +33,7 @@ enum class HidController : std::size_t { | |||
| 33 | NPad, | 33 | NPad, |
| 34 | Gesture, | 34 | Gesture, |
| 35 | ConsoleSixAxisSensor, | 35 | ConsoleSixAxisSensor, |
| 36 | Palma, | ||
| 36 | 37 | ||
| 37 | MaxControllers, | 38 | MaxControllers, |
| 38 | }; | 39 | }; |
| @@ -166,8 +167,36 @@ private: | |||
| 166 | void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); | 167 | void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 167 | void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); | 168 | void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); |
| 168 | void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx); | 169 | void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx); |
| 170 | void GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx); | ||
| 171 | void InitializePalma(Kernel::HLERequestContext& ctx); | ||
| 172 | void AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx); | ||
| 173 | void GetPalmaOperationInfo(Kernel::HLERequestContext& ctx); | ||
| 174 | void PlayPalmaActivity(Kernel::HLERequestContext& ctx); | ||
| 175 | void SetPalmaFrModeType(Kernel::HLERequestContext& ctx); | ||
| 176 | void ReadPalmaStep(Kernel::HLERequestContext& ctx); | ||
| 177 | void EnablePalmaStep(Kernel::HLERequestContext& ctx); | ||
| 178 | void ResetPalmaStep(Kernel::HLERequestContext& ctx); | ||
| 179 | void ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx); | ||
| 180 | void WritePalmaApplicationSection(Kernel::HLERequestContext& ctx); | ||
| 181 | void ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx); | ||
| 182 | void SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx); | ||
| 183 | void WritePalmaActivityEntry(Kernel::HLERequestContext& ctx); | ||
| 184 | void WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx); | ||
| 185 | void WritePalmaWaveEntry(Kernel::HLERequestContext& ctx); | ||
| 186 | void SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx); | ||
| 187 | void GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx); | ||
| 188 | void SuspendPalmaFeature(Kernel::HLERequestContext& ctx); | ||
| 189 | void GetPalmaOperationResult(Kernel::HLERequestContext& ctx); | ||
| 190 | void ReadPalmaPlayLog(Kernel::HLERequestContext& ctx); | ||
| 191 | void ResetPalmaPlayLog(Kernel::HLERequestContext& ctx); | ||
| 169 | void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); | 192 | void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); |
| 193 | void SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx); | ||
| 194 | void PairPalma(Kernel::HLERequestContext& ctx); | ||
| 170 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); | 195 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); |
| 196 | void CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx); | ||
| 197 | void EnablePalmaBoostMode(Kernel::HLERequestContext& ctx); | ||
| 198 | void GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx); | ||
| 199 | void SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx); | ||
| 171 | void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); | 200 | void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); |
| 172 | void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); | 201 | void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); |
| 173 | void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); | 202 | void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index c4b44cbf9..6a3453457 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp | |||
| @@ -542,7 +542,8 @@ Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_h | |||
| 542 | 542 | ||
| 543 | Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry( | 543 | Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry( |
| 544 | const Core::IrSensor::IrCameraHandle& camera_handle) { | 544 | const Core::IrSensor::IrCameraHandle& camera_handle) { |
| 545 | ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id"); | 545 | const auto npad_id_max_index = static_cast<u8>(sizeof(StatusManager::device)); |
| 546 | ASSERT_MSG(camera_handle.npad_id < npad_id_max_index, "invalid npad_id"); | ||
| 546 | return shared_memory->device[camera_handle.npad_id]; | 547 | return shared_memory->device[camera_handle.npad_id]; |
| 547 | } | 548 | } |
| 548 | 549 | ||
diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp new file mode 100644 index 000000000..8f3c04550 --- /dev/null +++ b/src/core/hle/service/ldn/lan_discovery.cpp | |||
| @@ -0,0 +1,633 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/service/ldn/lan_discovery.h" | ||
| 5 | #include "core/internal_network/network.h" | ||
| 6 | #include "core/internal_network/network_interface.h" | ||
| 7 | |||
| 8 | namespace Service::LDN { | ||
| 9 | |||
| 10 | LanStation::LanStation(s8 node_id_, LANDiscovery* discovery_) | ||
| 11 | : node_info(nullptr), status(NodeStatus::Disconnected), node_id(node_id_), | ||
| 12 | discovery(discovery_) {} | ||
| 13 | |||
| 14 | LanStation::~LanStation() = default; | ||
| 15 | |||
| 16 | NodeStatus LanStation::GetStatus() const { | ||
| 17 | return status; | ||
| 18 | } | ||
| 19 | |||
| 20 | void LanStation::OnClose() { | ||
| 21 | LOG_INFO(Service_LDN, "OnClose {}", node_id); | ||
| 22 | Reset(); | ||
| 23 | discovery->UpdateNodes(); | ||
| 24 | } | ||
| 25 | |||
| 26 | void LanStation::Reset() { | ||
| 27 | status = NodeStatus::Disconnected; | ||
| 28 | }; | ||
| 29 | |||
| 30 | void LanStation::OverrideInfo() { | ||
| 31 | bool connected = GetStatus() == NodeStatus::Connected; | ||
| 32 | node_info->node_id = node_id; | ||
| 33 | node_info->is_connected = connected ? 1 : 0; | ||
| 34 | } | ||
| 35 | |||
| 36 | LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_) | ||
| 37 | : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}), | ||
| 38 | room_network{room_network_} {} | ||
| 39 | |||
| 40 | LANDiscovery::~LANDiscovery() { | ||
| 41 | if (inited) { | ||
| 42 | Result rc = Finalize(); | ||
| 43 | LOG_INFO(Service_LDN, "Finalize: {}", rc.raw); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | void LANDiscovery::InitNetworkInfo() { | ||
| 48 | network_info.common.bssid = GetFakeMac(); | ||
| 49 | network_info.common.channel = WifiChannel::Wifi24_6; | ||
| 50 | network_info.common.link_level = LinkLevel::Good; | ||
| 51 | network_info.common.network_type = PackedNetworkType::Ldn; | ||
| 52 | network_info.common.ssid = fake_ssid; | ||
| 53 | |||
| 54 | auto& nodes = network_info.ldn.nodes; | ||
| 55 | for (std::size_t i = 0; i < NodeCountMax; i++) { | ||
| 56 | nodes[i].node_id = static_cast<s8>(i); | ||
| 57 | nodes[i].is_connected = 0; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | void LANDiscovery::InitNodeStateChange() { | ||
| 62 | for (auto& node_update : node_changes) { | ||
| 63 | node_update.state_change = NodeStateChange::None; | ||
| 64 | } | ||
| 65 | for (auto& node_state : node_last_states) { | ||
| 66 | node_state = 0; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | State LANDiscovery::GetState() const { | ||
| 71 | return state; | ||
| 72 | } | ||
| 73 | |||
| 74 | void LANDiscovery::SetState(State new_state) { | ||
| 75 | state = new_state; | ||
| 76 | } | ||
| 77 | |||
| 78 | Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const { | ||
| 79 | if (state == State::AccessPointCreated || state == State::StationConnected) { | ||
| 80 | std::memcpy(&out_network, &network_info, sizeof(network_info)); | ||
| 81 | return ResultSuccess; | ||
| 82 | } | ||
| 83 | |||
| 84 | return ResultBadState; | ||
| 85 | } | ||
| 86 | |||
| 87 | Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network, | ||
| 88 | std::vector<NodeLatestUpdate>& out_updates, | ||
| 89 | std::size_t buffer_count) { | ||
| 90 | if (buffer_count > NodeCountMax) { | ||
| 91 | return ResultInvalidBufferCount; | ||
| 92 | } | ||
| 93 | |||
| 94 | if (state == State::AccessPointCreated || state == State::StationConnected) { | ||
| 95 | std::memcpy(&out_network, &network_info, sizeof(network_info)); | ||
| 96 | for (std::size_t i = 0; i < buffer_count; i++) { | ||
| 97 | out_updates[i].state_change = node_changes[i].state_change; | ||
| 98 | node_changes[i].state_change = NodeStateChange::None; | ||
| 99 | } | ||
| 100 | return ResultSuccess; | ||
| 101 | } | ||
| 102 | |||
| 103 | return ResultBadState; | ||
| 104 | } | ||
| 105 | |||
| 106 | DisconnectReason LANDiscovery::GetDisconnectReason() const { | ||
| 107 | return disconnect_reason; | ||
| 108 | } | ||
| 109 | |||
| 110 | Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count, | ||
| 111 | const ScanFilter& filter) { | ||
| 112 | if (!IsFlagSet(filter.flag, ScanFilterFlag::NetworkType) || | ||
| 113 | filter.network_type <= NetworkType::All) { | ||
| 114 | if (!IsFlagSet(filter.flag, ScanFilterFlag::Ssid) && filter.ssid.length >= SsidLengthMax) { | ||
| 115 | return ResultBadInput; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | { | ||
| 120 | std::scoped_lock lock{packet_mutex}; | ||
| 121 | scan_results.clear(); | ||
| 122 | |||
| 123 | SendBroadcast(Network::LDNPacketType::Scan); | ||
| 124 | } | ||
| 125 | |||
| 126 | LOG_INFO(Service_LDN, "Waiting for scan replies"); | ||
| 127 | std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
| 128 | |||
| 129 | std::scoped_lock lock{packet_mutex}; | ||
| 130 | for (const auto& [key, info] : scan_results) { | ||
| 131 | if (count >= networks.size()) { | ||
| 132 | break; | ||
| 133 | } | ||
| 134 | |||
| 135 | if (IsFlagSet(filter.flag, ScanFilterFlag::LocalCommunicationId)) { | ||
| 136 | if (filter.network_id.intent_id.local_communication_id != | ||
| 137 | info.network_id.intent_id.local_communication_id) { | ||
| 138 | continue; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | if (IsFlagSet(filter.flag, ScanFilterFlag::SessionId)) { | ||
| 142 | if (filter.network_id.session_id != info.network_id.session_id) { | ||
| 143 | continue; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | if (IsFlagSet(filter.flag, ScanFilterFlag::NetworkType)) { | ||
| 147 | if (filter.network_type != static_cast<NetworkType>(info.common.network_type)) { | ||
| 148 | continue; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | if (IsFlagSet(filter.flag, ScanFilterFlag::Ssid)) { | ||
| 152 | if (filter.ssid != info.common.ssid) { | ||
| 153 | continue; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | if (IsFlagSet(filter.flag, ScanFilterFlag::SceneId)) { | ||
| 157 | if (filter.network_id.intent_id.scene_id != info.network_id.intent_id.scene_id) { | ||
| 158 | continue; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | networks[count++] = info; | ||
| 163 | } | ||
| 164 | |||
| 165 | return ResultSuccess; | ||
| 166 | } | ||
| 167 | |||
| 168 | Result LANDiscovery::SetAdvertiseData(std::span<const u8> data) { | ||
| 169 | std::scoped_lock lock{packet_mutex}; | ||
| 170 | const std::size_t size = data.size(); | ||
| 171 | if (size > AdvertiseDataSizeMax) { | ||
| 172 | return ResultAdvertiseDataTooLarge; | ||
| 173 | } | ||
| 174 | |||
| 175 | std::memcpy(network_info.ldn.advertise_data.data(), data.data(), size); | ||
| 176 | network_info.ldn.advertise_data_size = static_cast<u16>(size); | ||
| 177 | |||
| 178 | UpdateNodes(); | ||
| 179 | |||
| 180 | return ResultSuccess; | ||
| 181 | } | ||
| 182 | |||
| 183 | Result LANDiscovery::OpenAccessPoint() { | ||
| 184 | std::scoped_lock lock{packet_mutex}; | ||
| 185 | disconnect_reason = DisconnectReason::None; | ||
| 186 | if (state == State::None) { | ||
| 187 | return ResultBadState; | ||
| 188 | } | ||
| 189 | |||
| 190 | ResetStations(); | ||
| 191 | SetState(State::AccessPointOpened); | ||
| 192 | |||
| 193 | return ResultSuccess; | ||
| 194 | } | ||
| 195 | |||
| 196 | Result LANDiscovery::CloseAccessPoint() { | ||
| 197 | std::scoped_lock lock{packet_mutex}; | ||
| 198 | if (state == State::None) { | ||
| 199 | return ResultBadState; | ||
| 200 | } | ||
| 201 | |||
| 202 | if (state == State::AccessPointCreated) { | ||
| 203 | DestroyNetwork(); | ||
| 204 | } | ||
| 205 | |||
| 206 | ResetStations(); | ||
| 207 | SetState(State::Initialized); | ||
| 208 | |||
| 209 | return ResultSuccess; | ||
| 210 | } | ||
| 211 | |||
| 212 | Result LANDiscovery::OpenStation() { | ||
| 213 | std::scoped_lock lock{packet_mutex}; | ||
| 214 | disconnect_reason = DisconnectReason::None; | ||
| 215 | if (state == State::None) { | ||
| 216 | return ResultBadState; | ||
| 217 | } | ||
| 218 | |||
| 219 | ResetStations(); | ||
| 220 | SetState(State::StationOpened); | ||
| 221 | |||
| 222 | return ResultSuccess; | ||
| 223 | } | ||
| 224 | |||
| 225 | Result LANDiscovery::CloseStation() { | ||
| 226 | std::scoped_lock lock{packet_mutex}; | ||
| 227 | if (state == State::None) { | ||
| 228 | return ResultBadState; | ||
| 229 | } | ||
| 230 | |||
| 231 | if (state == State::StationConnected) { | ||
| 232 | Disconnect(); | ||
| 233 | } | ||
| 234 | |||
| 235 | ResetStations(); | ||
| 236 | SetState(State::Initialized); | ||
| 237 | |||
| 238 | return ResultSuccess; | ||
| 239 | } | ||
| 240 | |||
| 241 | Result LANDiscovery::CreateNetwork(const SecurityConfig& security_config, | ||
| 242 | const UserConfig& user_config, | ||
| 243 | const NetworkConfig& network_config) { | ||
| 244 | std::scoped_lock lock{packet_mutex}; | ||
| 245 | |||
| 246 | if (state != State::AccessPointOpened) { | ||
| 247 | return ResultBadState; | ||
| 248 | } | ||
| 249 | |||
| 250 | InitNetworkInfo(); | ||
| 251 | network_info.ldn.node_count_max = network_config.node_count_max; | ||
| 252 | network_info.ldn.security_mode = security_config.security_mode; | ||
| 253 | |||
| 254 | if (network_config.channel == WifiChannel::Default) { | ||
| 255 | network_info.common.channel = WifiChannel::Wifi24_6; | ||
| 256 | } else { | ||
| 257 | network_info.common.channel = network_config.channel; | ||
| 258 | } | ||
| 259 | |||
| 260 | std::independent_bits_engine<std::mt19937, 64, u64> bits_engine; | ||
| 261 | network_info.network_id.session_id.high = bits_engine(); | ||
| 262 | network_info.network_id.session_id.low = bits_engine(); | ||
| 263 | network_info.network_id.intent_id = network_config.intent_id; | ||
| 264 | |||
| 265 | NodeInfo& node0 = network_info.ldn.nodes[0]; | ||
| 266 | const Result rc2 = GetNodeInfo(node0, user_config, network_config.local_communication_version); | ||
| 267 | if (rc2.IsError()) { | ||
| 268 | return ResultAccessPointConnectionFailed; | ||
| 269 | } | ||
| 270 | |||
| 271 | SetState(State::AccessPointCreated); | ||
| 272 | |||
| 273 | InitNodeStateChange(); | ||
| 274 | node0.is_connected = 1; | ||
| 275 | UpdateNodes(); | ||
| 276 | |||
| 277 | return rc2; | ||
| 278 | } | ||
| 279 | |||
| 280 | Result LANDiscovery::DestroyNetwork() { | ||
| 281 | for (auto local_ip : connected_clients) { | ||
| 282 | SendPacket(Network::LDNPacketType::DestroyNetwork, local_ip); | ||
| 283 | } | ||
| 284 | |||
| 285 | ResetStations(); | ||
| 286 | |||
| 287 | SetState(State::AccessPointOpened); | ||
| 288 | lan_event(); | ||
| 289 | |||
| 290 | return ResultSuccess; | ||
| 291 | } | ||
| 292 | |||
| 293 | Result LANDiscovery::Connect(const NetworkInfo& network_info_, const UserConfig& user_config, | ||
| 294 | u16 local_communication_version) { | ||
| 295 | std::scoped_lock lock{packet_mutex}; | ||
| 296 | if (network_info_.ldn.node_count == 0) { | ||
| 297 | return ResultInvalidNodeCount; | ||
| 298 | } | ||
| 299 | |||
| 300 | Result rc = GetNodeInfo(node_info, user_config, local_communication_version); | ||
| 301 | if (rc.IsError()) { | ||
| 302 | return ResultConnectionFailed; | ||
| 303 | } | ||
| 304 | |||
| 305 | Ipv4Address node_host = network_info_.ldn.nodes[0].ipv4_address; | ||
| 306 | std::reverse(std::begin(node_host), std::end(node_host)); // htonl | ||
| 307 | host_ip = node_host; | ||
| 308 | SendPacket(Network::LDNPacketType::Connect, node_info, *host_ip); | ||
| 309 | |||
| 310 | InitNodeStateChange(); | ||
| 311 | |||
| 312 | std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
| 313 | |||
| 314 | return ResultSuccess; | ||
| 315 | } | ||
| 316 | |||
| 317 | Result LANDiscovery::Disconnect() { | ||
| 318 | if (host_ip) { | ||
| 319 | SendPacket(Network::LDNPacketType::Disconnect, node_info, *host_ip); | ||
| 320 | } | ||
| 321 | |||
| 322 | SetState(State::StationOpened); | ||
| 323 | lan_event(); | ||
| 324 | |||
| 325 | return ResultSuccess; | ||
| 326 | } | ||
| 327 | |||
| 328 | Result LANDiscovery::Initialize(LanEventFunc lan_event_, bool listening) { | ||
| 329 | std::scoped_lock lock{packet_mutex}; | ||
| 330 | if (inited) { | ||
| 331 | return ResultSuccess; | ||
| 332 | } | ||
| 333 | |||
| 334 | for (auto& station : stations) { | ||
| 335 | station.discovery = this; | ||
| 336 | station.node_info = &network_info.ldn.nodes[station.node_id]; | ||
| 337 | station.Reset(); | ||
| 338 | } | ||
| 339 | |||
| 340 | connected_clients.clear(); | ||
| 341 | lan_event = lan_event_; | ||
| 342 | |||
| 343 | SetState(State::Initialized); | ||
| 344 | |||
| 345 | inited = true; | ||
| 346 | return ResultSuccess; | ||
| 347 | } | ||
| 348 | |||
| 349 | Result LANDiscovery::Finalize() { | ||
| 350 | std::scoped_lock lock{packet_mutex}; | ||
| 351 | Result rc = ResultSuccess; | ||
| 352 | |||
| 353 | if (inited) { | ||
| 354 | if (state == State::AccessPointCreated) { | ||
| 355 | DestroyNetwork(); | ||
| 356 | } | ||
| 357 | if (state == State::StationConnected) { | ||
| 358 | Disconnect(); | ||
| 359 | } | ||
| 360 | |||
| 361 | ResetStations(); | ||
| 362 | inited = false; | ||
| 363 | } | ||
| 364 | |||
| 365 | SetState(State::None); | ||
| 366 | |||
| 367 | return rc; | ||
| 368 | } | ||
| 369 | |||
| 370 | void LANDiscovery::ResetStations() { | ||
| 371 | for (auto& station : stations) { | ||
| 372 | station.Reset(); | ||
| 373 | } | ||
| 374 | connected_clients.clear(); | ||
| 375 | } | ||
| 376 | |||
| 377 | void LANDiscovery::UpdateNodes() { | ||
| 378 | u8 count = 0; | ||
| 379 | for (auto& station : stations) { | ||
| 380 | bool connected = station.GetStatus() == NodeStatus::Connected; | ||
| 381 | if (connected) { | ||
| 382 | count++; | ||
| 383 | } | ||
| 384 | station.OverrideInfo(); | ||
| 385 | } | ||
| 386 | network_info.ldn.node_count = count + 1; | ||
| 387 | |||
| 388 | for (auto local_ip : connected_clients) { | ||
| 389 | SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip); | ||
| 390 | } | ||
| 391 | |||
| 392 | OnNetworkInfoChanged(); | ||
| 393 | } | ||
| 394 | |||
| 395 | void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) { | ||
| 396 | network_info = info; | ||
| 397 | if (state == State::StationOpened) { | ||
| 398 | SetState(State::StationConnected); | ||
| 399 | } | ||
| 400 | OnNetworkInfoChanged(); | ||
| 401 | } | ||
| 402 | |||
| 403 | void LANDiscovery::OnDisconnectFromHost() { | ||
| 404 | LOG_INFO(Service_LDN, "OnDisconnectFromHost state: {}", static_cast<int>(state)); | ||
| 405 | host_ip = std::nullopt; | ||
| 406 | if (state == State::StationConnected) { | ||
| 407 | SetState(State::StationOpened); | ||
| 408 | lan_event(); | ||
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | void LANDiscovery::OnNetworkInfoChanged() { | ||
| 413 | if (IsNodeStateChanged()) { | ||
| 414 | lan_event(); | ||
| 415 | } | ||
| 416 | return; | ||
| 417 | } | ||
| 418 | |||
| 419 | Network::IPv4Address LANDiscovery::GetLocalIp() const { | ||
| 420 | Network::IPv4Address local_ip{0xFF, 0xFF, 0xFF, 0xFF}; | ||
| 421 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 422 | if (room_member->IsConnected()) { | ||
| 423 | local_ip = room_member->GetFakeIpAddress(); | ||
| 424 | } | ||
| 425 | } | ||
| 426 | return local_ip; | ||
| 427 | } | ||
| 428 | |||
| 429 | template <typename Data> | ||
| 430 | void LANDiscovery::SendPacket(Network::LDNPacketType type, const Data& data, | ||
| 431 | Ipv4Address remote_ip) { | ||
| 432 | Network::LDNPacket packet; | ||
| 433 | packet.type = type; | ||
| 434 | |||
| 435 | packet.broadcast = false; | ||
| 436 | packet.local_ip = GetLocalIp(); | ||
| 437 | packet.remote_ip = remote_ip; | ||
| 438 | |||
| 439 | packet.data.resize(sizeof(data)); | ||
| 440 | std::memcpy(packet.data.data(), &data, sizeof(data)); | ||
| 441 | SendPacket(packet); | ||
| 442 | } | ||
| 443 | |||
| 444 | void LANDiscovery::SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip) { | ||
| 445 | Network::LDNPacket packet; | ||
| 446 | packet.type = type; | ||
| 447 | |||
| 448 | packet.broadcast = false; | ||
| 449 | packet.local_ip = GetLocalIp(); | ||
| 450 | packet.remote_ip = remote_ip; | ||
| 451 | |||
| 452 | SendPacket(packet); | ||
| 453 | } | ||
| 454 | |||
| 455 | template <typename Data> | ||
| 456 | void LANDiscovery::SendBroadcast(Network::LDNPacketType type, const Data& data) { | ||
| 457 | Network::LDNPacket packet; | ||
| 458 | packet.type = type; | ||
| 459 | |||
| 460 | packet.broadcast = true; | ||
| 461 | packet.local_ip = GetLocalIp(); | ||
| 462 | |||
| 463 | packet.data.resize(sizeof(data)); | ||
| 464 | std::memcpy(packet.data.data(), &data, sizeof(data)); | ||
| 465 | SendPacket(packet); | ||
| 466 | } | ||
| 467 | |||
| 468 | void LANDiscovery::SendBroadcast(Network::LDNPacketType type) { | ||
| 469 | Network::LDNPacket packet; | ||
| 470 | packet.type = type; | ||
| 471 | |||
| 472 | packet.broadcast = true; | ||
| 473 | packet.local_ip = GetLocalIp(); | ||
| 474 | |||
| 475 | SendPacket(packet); | ||
| 476 | } | ||
| 477 | |||
| 478 | void LANDiscovery::SendPacket(const Network::LDNPacket& packet) { | ||
| 479 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 480 | if (room_member->IsConnected()) { | ||
| 481 | room_member->SendLdnPacket(packet); | ||
| 482 | } | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) { | ||
| 487 | std::scoped_lock lock{packet_mutex}; | ||
| 488 | switch (packet.type) { | ||
| 489 | case Network::LDNPacketType::Scan: { | ||
| 490 | LOG_INFO(Frontend, "Scan packet received!"); | ||
| 491 | if (state == State::AccessPointCreated) { | ||
| 492 | // Reply to the sender | ||
| 493 | SendPacket(Network::LDNPacketType::ScanResp, network_info, packet.local_ip); | ||
| 494 | } | ||
| 495 | break; | ||
| 496 | } | ||
| 497 | case Network::LDNPacketType::ScanResp: { | ||
| 498 | LOG_INFO(Frontend, "ScanResp packet received!"); | ||
| 499 | |||
| 500 | NetworkInfo info{}; | ||
| 501 | std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); | ||
| 502 | scan_results.insert({info.common.bssid, info}); | ||
| 503 | |||
| 504 | break; | ||
| 505 | } | ||
| 506 | case Network::LDNPacketType::Connect: { | ||
| 507 | LOG_INFO(Frontend, "Connect packet received!"); | ||
| 508 | |||
| 509 | NodeInfo info{}; | ||
| 510 | std::memcpy(&info, packet.data.data(), sizeof(NodeInfo)); | ||
| 511 | |||
| 512 | connected_clients.push_back(packet.local_ip); | ||
| 513 | |||
| 514 | for (LanStation& station : stations) { | ||
| 515 | if (station.status != NodeStatus::Connected) { | ||
| 516 | *station.node_info = info; | ||
| 517 | station.status = NodeStatus::Connected; | ||
| 518 | break; | ||
| 519 | } | ||
| 520 | } | ||
| 521 | |||
| 522 | UpdateNodes(); | ||
| 523 | |||
| 524 | break; | ||
| 525 | } | ||
| 526 | case Network::LDNPacketType::Disconnect: { | ||
| 527 | LOG_INFO(Frontend, "Disconnect packet received!"); | ||
| 528 | |||
| 529 | connected_clients.erase( | ||
| 530 | std::remove(connected_clients.begin(), connected_clients.end(), packet.local_ip), | ||
| 531 | connected_clients.end()); | ||
| 532 | |||
| 533 | NodeInfo info{}; | ||
| 534 | std::memcpy(&info, packet.data.data(), sizeof(NodeInfo)); | ||
| 535 | |||
| 536 | for (LanStation& station : stations) { | ||
| 537 | if (station.status == NodeStatus::Connected && | ||
| 538 | station.node_info->mac_address == info.mac_address) { | ||
| 539 | station.OnClose(); | ||
| 540 | break; | ||
| 541 | } | ||
| 542 | } | ||
| 543 | |||
| 544 | break; | ||
| 545 | } | ||
| 546 | case Network::LDNPacketType::DestroyNetwork: { | ||
| 547 | ResetStations(); | ||
| 548 | OnDisconnectFromHost(); | ||
| 549 | break; | ||
| 550 | } | ||
| 551 | case Network::LDNPacketType::SyncNetwork: { | ||
| 552 | if (state == State::StationOpened || state == State::StationConnected) { | ||
| 553 | LOG_INFO(Frontend, "SyncNetwork packet received!"); | ||
| 554 | NetworkInfo info{}; | ||
| 555 | std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); | ||
| 556 | |||
| 557 | OnSyncNetwork(info); | ||
| 558 | } else { | ||
| 559 | LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!"); | ||
| 560 | } | ||
| 561 | |||
| 562 | break; | ||
| 563 | } | ||
| 564 | default: { | ||
| 565 | LOG_INFO(Frontend, "ReceivePacket unhandled type {}", static_cast<int>(packet.type)); | ||
| 566 | break; | ||
| 567 | } | ||
| 568 | } | ||
| 569 | } | ||
| 570 | |||
| 571 | bool LANDiscovery::IsNodeStateChanged() { | ||
| 572 | bool changed = false; | ||
| 573 | const auto& nodes = network_info.ldn.nodes; | ||
| 574 | for (int i = 0; i < NodeCountMax; i++) { | ||
| 575 | if (nodes[i].is_connected != node_last_states[i]) { | ||
| 576 | if (nodes[i].is_connected) { | ||
| 577 | node_changes[i].state_change |= NodeStateChange::Connect; | ||
| 578 | } else { | ||
| 579 | node_changes[i].state_change |= NodeStateChange::Disconnect; | ||
| 580 | } | ||
| 581 | node_last_states[i] = nodes[i].is_connected; | ||
| 582 | changed = true; | ||
| 583 | } | ||
| 584 | } | ||
| 585 | return changed; | ||
| 586 | } | ||
| 587 | |||
| 588 | bool LANDiscovery::IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const { | ||
| 589 | const auto flag_value = static_cast<u32>(flag); | ||
| 590 | const auto search_flag_value = static_cast<u32>(search_flag); | ||
| 591 | return (flag_value & search_flag_value) == search_flag_value; | ||
| 592 | } | ||
| 593 | |||
| 594 | int LANDiscovery::GetStationCount() const { | ||
| 595 | return static_cast<int>( | ||
| 596 | std::count_if(stations.begin(), stations.end(), [](const auto& station) { | ||
| 597 | return station.GetStatus() != NodeStatus::Disconnected; | ||
| 598 | })); | ||
| 599 | } | ||
| 600 | |||
| 601 | MacAddress LANDiscovery::GetFakeMac() const { | ||
| 602 | MacAddress mac{}; | ||
| 603 | mac.raw[0] = 0x02; | ||
| 604 | mac.raw[1] = 0x00; | ||
| 605 | |||
| 606 | const auto ip = GetLocalIp(); | ||
| 607 | memcpy(mac.raw.data() + 2, &ip, sizeof(ip)); | ||
| 608 | |||
| 609 | return mac; | ||
| 610 | } | ||
| 611 | |||
| 612 | Result LANDiscovery::GetNodeInfo(NodeInfo& node, const UserConfig& userConfig, | ||
| 613 | u16 localCommunicationVersion) { | ||
| 614 | const auto network_interface = Network::GetSelectedNetworkInterface(); | ||
| 615 | |||
| 616 | if (!network_interface) { | ||
| 617 | LOG_ERROR(Service_LDN, "No network interface available"); | ||
| 618 | return ResultNoIpAddress; | ||
| 619 | } | ||
| 620 | |||
| 621 | node.mac_address = GetFakeMac(); | ||
| 622 | node.is_connected = 1; | ||
| 623 | std::memcpy(node.user_name.data(), userConfig.user_name.data(), UserNameBytesMax + 1); | ||
| 624 | node.local_communication_version = localCommunicationVersion; | ||
| 625 | |||
| 626 | Ipv4Address current_address = GetLocalIp(); | ||
| 627 | std::reverse(std::begin(current_address), std::end(current_address)); // ntohl | ||
| 628 | node.ipv4_address = current_address; | ||
| 629 | |||
| 630 | return ResultSuccess; | ||
| 631 | } | ||
| 632 | |||
| 633 | } // namespace Service::LDN | ||
diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h new file mode 100644 index 000000000..3833cd764 --- /dev/null +++ b/src/core/hle/service/ldn/lan_discovery.h | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <cstring> | ||
| 8 | #include <functional> | ||
| 9 | #include <memory> | ||
| 10 | #include <mutex> | ||
| 11 | #include <optional> | ||
| 12 | #include <random> | ||
| 13 | #include <span> | ||
| 14 | #include <thread> | ||
| 15 | #include <unordered_map> | ||
| 16 | |||
| 17 | #include "common/logging/log.h" | ||
| 18 | #include "common/socket_types.h" | ||
| 19 | #include "core/hle/result.h" | ||
| 20 | #include "core/hle/service/ldn/ldn_results.h" | ||
| 21 | #include "core/hle/service/ldn/ldn_types.h" | ||
| 22 | #include "network/network.h" | ||
| 23 | |||
| 24 | namespace Service::LDN { | ||
| 25 | |||
| 26 | class LANDiscovery; | ||
| 27 | |||
| 28 | class LanStation { | ||
| 29 | public: | ||
| 30 | LanStation(s8 node_id_, LANDiscovery* discovery_); | ||
| 31 | ~LanStation(); | ||
| 32 | |||
| 33 | void OnClose(); | ||
| 34 | NodeStatus GetStatus() const; | ||
| 35 | void Reset(); | ||
| 36 | void OverrideInfo(); | ||
| 37 | |||
| 38 | protected: | ||
| 39 | friend class LANDiscovery; | ||
| 40 | NodeInfo* node_info; | ||
| 41 | NodeStatus status; | ||
| 42 | s8 node_id; | ||
| 43 | LANDiscovery* discovery; | ||
| 44 | }; | ||
| 45 | |||
| 46 | class LANDiscovery { | ||
| 47 | public: | ||
| 48 | using LanEventFunc = std::function<void()>; | ||
| 49 | |||
| 50 | LANDiscovery(Network::RoomNetwork& room_network_); | ||
| 51 | ~LANDiscovery(); | ||
| 52 | |||
| 53 | State GetState() const; | ||
| 54 | void SetState(State new_state); | ||
| 55 | |||
| 56 | Result GetNetworkInfo(NetworkInfo& out_network) const; | ||
| 57 | Result GetNetworkInfo(NetworkInfo& out_network, std::vector<NodeLatestUpdate>& out_updates, | ||
| 58 | std::size_t buffer_count); | ||
| 59 | |||
| 60 | DisconnectReason GetDisconnectReason() const; | ||
| 61 | Result Scan(std::vector<NetworkInfo>& networks, u16& count, const ScanFilter& filter); | ||
| 62 | Result SetAdvertiseData(std::span<const u8> data); | ||
| 63 | |||
| 64 | Result OpenAccessPoint(); | ||
| 65 | Result CloseAccessPoint(); | ||
| 66 | |||
| 67 | Result OpenStation(); | ||
| 68 | Result CloseStation(); | ||
| 69 | |||
| 70 | Result CreateNetwork(const SecurityConfig& security_config, const UserConfig& user_config, | ||
| 71 | const NetworkConfig& network_config); | ||
| 72 | Result DestroyNetwork(); | ||
| 73 | |||
| 74 | Result Connect(const NetworkInfo& network_info_, const UserConfig& user_config, | ||
| 75 | u16 local_communication_version); | ||
| 76 | Result Disconnect(); | ||
| 77 | |||
| 78 | Result Initialize(LanEventFunc lan_event_ = empty_func, bool listening = true); | ||
| 79 | Result Finalize(); | ||
| 80 | |||
| 81 | void ReceivePacket(const Network::LDNPacket& packet); | ||
| 82 | |||
| 83 | protected: | ||
| 84 | friend class LanStation; | ||
| 85 | |||
| 86 | void InitNetworkInfo(); | ||
| 87 | void InitNodeStateChange(); | ||
| 88 | |||
| 89 | void ResetStations(); | ||
| 90 | void UpdateNodes(); | ||
| 91 | |||
| 92 | void OnSyncNetwork(const NetworkInfo& info); | ||
| 93 | void OnDisconnectFromHost(); | ||
| 94 | void OnNetworkInfoChanged(); | ||
| 95 | |||
| 96 | bool IsNodeStateChanged(); | ||
| 97 | bool IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const; | ||
| 98 | int GetStationCount() const; | ||
| 99 | MacAddress GetFakeMac() const; | ||
| 100 | Result GetNodeInfo(NodeInfo& node, const UserConfig& user_config, | ||
| 101 | u16 local_communication_version); | ||
| 102 | |||
| 103 | Network::IPv4Address GetLocalIp() const; | ||
| 104 | template <typename Data> | ||
| 105 | void SendPacket(Network::LDNPacketType type, const Data& data, Ipv4Address remote_ip); | ||
| 106 | void SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip); | ||
| 107 | template <typename Data> | ||
| 108 | void SendBroadcast(Network::LDNPacketType type, const Data& data); | ||
| 109 | void SendBroadcast(Network::LDNPacketType type); | ||
| 110 | void SendPacket(const Network::LDNPacket& packet); | ||
| 111 | |||
| 112 | static const LanEventFunc empty_func; | ||
| 113 | static constexpr Ssid fake_ssid{"YuzuFakeSsidForLdn"}; | ||
| 114 | |||
| 115 | bool inited{}; | ||
| 116 | std::mutex packet_mutex; | ||
| 117 | std::array<LanStation, StationCountMax> stations; | ||
| 118 | std::array<NodeLatestUpdate, NodeCountMax> node_changes{}; | ||
| 119 | std::array<u8, NodeCountMax> node_last_states{}; | ||
| 120 | std::unordered_map<MacAddress, NetworkInfo, MACAddressHash> scan_results{}; | ||
| 121 | NodeInfo node_info{}; | ||
| 122 | NetworkInfo network_info{}; | ||
| 123 | State state{State::None}; | ||
| 124 | DisconnectReason disconnect_reason{DisconnectReason::None}; | ||
| 125 | |||
| 126 | // TODO (flTobi): Should this be an std::set? | ||
| 127 | std::vector<Ipv4Address> connected_clients; | ||
| 128 | std::optional<Ipv4Address> host_ip; | ||
| 129 | |||
| 130 | LanEventFunc lan_event; | ||
| 131 | |||
| 132 | Network::RoomNetwork& room_network; | ||
| 133 | }; | ||
| 134 | } // namespace Service::LDN | ||
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index c11daff54..ea3e7e55a 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp | |||
| @@ -4,11 +4,13 @@ | |||
| 4 | #include <memory> | 4 | #include <memory> |
| 5 | 5 | ||
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "core/hle/service/ldn/lan_discovery.h" | ||
| 7 | #include "core/hle/service/ldn/ldn.h" | 8 | #include "core/hle/service/ldn/ldn.h" |
| 8 | #include "core/hle/service/ldn/ldn_results.h" | 9 | #include "core/hle/service/ldn/ldn_results.h" |
| 9 | #include "core/hle/service/ldn/ldn_types.h" | 10 | #include "core/hle/service/ldn/ldn_types.h" |
| 10 | #include "core/internal_network/network.h" | 11 | #include "core/internal_network/network.h" |
| 11 | #include "core/internal_network/network_interface.h" | 12 | #include "core/internal_network/network_interface.h" |
| 13 | #include "network/network.h" | ||
| 12 | 14 | ||
| 13 | // This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent | 15 | // This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent |
| 14 | #undef CreateEvent | 16 | #undef CreateEvent |
| @@ -105,13 +107,13 @@ class IUserLocalCommunicationService final | |||
| 105 | public: | 107 | public: |
| 106 | explicit IUserLocalCommunicationService(Core::System& system_) | 108 | explicit IUserLocalCommunicationService(Core::System& system_) |
| 107 | : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew}, | 109 | : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew}, |
| 108 | service_context{system, "IUserLocalCommunicationService"}, room_network{ | 110 | service_context{system, "IUserLocalCommunicationService"}, |
| 109 | system_.GetRoomNetwork()} { | 111 | room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} { |
| 110 | // clang-format off | 112 | // clang-format off |
| 111 | static const FunctionInfo functions[] = { | 113 | static const FunctionInfo functions[] = { |
| 112 | {0, &IUserLocalCommunicationService::GetState, "GetState"}, | 114 | {0, &IUserLocalCommunicationService::GetState, "GetState"}, |
| 113 | {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, | 115 | {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"}, |
| 114 | {2, nullptr, "GetIpv4Address"}, | 116 | {2, &IUserLocalCommunicationService::GetIpv4Address, "GetIpv4Address"}, |
| 115 | {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"}, | 117 | {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"}, |
| 116 | {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"}, | 118 | {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"}, |
| 117 | {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"}, | 119 | {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"}, |
| @@ -119,7 +121,7 @@ public: | |||
| 119 | {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"}, | 121 | {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"}, |
| 120 | {102, &IUserLocalCommunicationService::Scan, "Scan"}, | 122 | {102, &IUserLocalCommunicationService::Scan, "Scan"}, |
| 121 | {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"}, | 123 | {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"}, |
| 122 | {104, nullptr, "SetWirelessControllerRestriction"}, | 124 | {104, &IUserLocalCommunicationService::SetWirelessControllerRestriction, "SetWirelessControllerRestriction"}, |
| 123 | {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, | 125 | {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"}, |
| 124 | {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, | 126 | {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"}, |
| 125 | {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, | 127 | {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"}, |
| @@ -148,16 +150,30 @@ public: | |||
| 148 | } | 150 | } |
| 149 | 151 | ||
| 150 | ~IUserLocalCommunicationService() { | 152 | ~IUserLocalCommunicationService() { |
| 153 | if (is_initialized) { | ||
| 154 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 155 | room_member->Unbind(ldn_packet_received); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 151 | service_context.CloseEvent(state_change_event); | 159 | service_context.CloseEvent(state_change_event); |
| 152 | } | 160 | } |
| 153 | 161 | ||
| 162 | /// Callback to parse and handle a received LDN packet. | ||
| 163 | void OnLDNPacketReceived(const Network::LDNPacket& packet) { | ||
| 164 | lan_discovery.ReceivePacket(packet); | ||
| 165 | } | ||
| 166 | |||
| 154 | void OnEventFired() { | 167 | void OnEventFired() { |
| 155 | state_change_event->GetWritableEvent().Signal(); | 168 | state_change_event->GetWritableEvent().Signal(); |
| 156 | } | 169 | } |
| 157 | 170 | ||
| 158 | void GetState(Kernel::HLERequestContext& ctx) { | 171 | void GetState(Kernel::HLERequestContext& ctx) { |
| 159 | State state = State::Error; | 172 | State state = State::Error; |
| 160 | LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state); | 173 | |
| 174 | if (is_initialized) { | ||
| 175 | state = lan_discovery.GetState(); | ||
| 176 | } | ||
| 161 | 177 | ||
| 162 | IPC::ResponseBuilder rb{ctx, 3}; | 178 | IPC::ResponseBuilder rb{ctx, 3}; |
| 163 | rb.Push(ResultSuccess); | 179 | rb.Push(ResultSuccess); |
| @@ -175,7 +191,7 @@ public: | |||
| 175 | } | 191 | } |
| 176 | 192 | ||
| 177 | NetworkInfo network_info{}; | 193 | NetworkInfo network_info{}; |
| 178 | const auto rc = ResultSuccess; | 194 | const auto rc = lan_discovery.GetNetworkInfo(network_info); |
| 179 | if (rc.IsError()) { | 195 | if (rc.IsError()) { |
| 180 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); | 196 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); |
| 181 | IPC::ResponseBuilder rb{ctx, 2}; | 197 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -183,28 +199,50 @@ public: | |||
| 183 | return; | 199 | return; |
| 184 | } | 200 | } |
| 185 | 201 | ||
| 186 | LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}", | ||
| 187 | network_info.common.ssid.GetStringValue(), network_info.ldn.node_count); | ||
| 188 | |||
| 189 | ctx.WriteBuffer<NetworkInfo>(network_info); | 202 | ctx.WriteBuffer<NetworkInfo>(network_info); |
| 190 | IPC::ResponseBuilder rb{ctx, 2}; | 203 | IPC::ResponseBuilder rb{ctx, 2}; |
| 191 | rb.Push(rc); | 204 | rb.Push(ResultSuccess); |
| 192 | } | 205 | } |
| 193 | 206 | ||
| 194 | void GetDisconnectReason(Kernel::HLERequestContext& ctx) { | 207 | void GetIpv4Address(Kernel::HLERequestContext& ctx) { |
| 195 | const auto disconnect_reason = DisconnectReason::None; | 208 | const auto network_interface = Network::GetSelectedNetworkInterface(); |
| 209 | |||
| 210 | if (!network_interface) { | ||
| 211 | LOG_ERROR(Service_LDN, "No network interface available"); | ||
| 212 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 213 | rb.Push(ResultNoIpAddress); | ||
| 214 | return; | ||
| 215 | } | ||
| 196 | 216 | ||
| 197 | LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason); | 217 | Ipv4Address current_address{Network::TranslateIPv4(network_interface->ip_address)}; |
| 218 | Ipv4Address subnet_mask{Network::TranslateIPv4(network_interface->subnet_mask)}; | ||
| 219 | |||
| 220 | // When we're connected to a room, spoof the hosts IP address | ||
| 221 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 222 | if (room_member->IsConnected()) { | ||
| 223 | current_address = room_member->GetFakeIpAddress(); | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | std::reverse(std::begin(current_address), std::end(current_address)); // ntohl | ||
| 228 | std::reverse(std::begin(subnet_mask), std::end(subnet_mask)); // ntohl | ||
| 229 | |||
| 230 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 231 | rb.Push(ResultSuccess); | ||
| 232 | rb.PushRaw(current_address); | ||
| 233 | rb.PushRaw(subnet_mask); | ||
| 234 | } | ||
| 198 | 235 | ||
| 236 | void GetDisconnectReason(Kernel::HLERequestContext& ctx) { | ||
| 199 | IPC::ResponseBuilder rb{ctx, 3}; | 237 | IPC::ResponseBuilder rb{ctx, 3}; |
| 200 | rb.Push(ResultSuccess); | 238 | rb.Push(ResultSuccess); |
| 201 | rb.PushEnum(disconnect_reason); | 239 | rb.PushEnum(lan_discovery.GetDisconnectReason()); |
| 202 | } | 240 | } |
| 203 | 241 | ||
| 204 | void GetSecurityParameter(Kernel::HLERequestContext& ctx) { | 242 | void GetSecurityParameter(Kernel::HLERequestContext& ctx) { |
| 205 | SecurityParameter security_parameter{}; | 243 | SecurityParameter security_parameter{}; |
| 206 | NetworkInfo info{}; | 244 | NetworkInfo info{}; |
| 207 | const Result rc = ResultSuccess; | 245 | const Result rc = lan_discovery.GetNetworkInfo(info); |
| 208 | 246 | ||
| 209 | if (rc.IsError()) { | 247 | if (rc.IsError()) { |
| 210 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); | 248 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); |
| @@ -217,8 +255,6 @@ public: | |||
| 217 | std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(), | 255 | std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(), |
| 218 | sizeof(SecurityParameter::data)); | 256 | sizeof(SecurityParameter::data)); |
| 219 | 257 | ||
| 220 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 221 | |||
| 222 | IPC::ResponseBuilder rb{ctx, 10}; | 258 | IPC::ResponseBuilder rb{ctx, 10}; |
| 223 | rb.Push(rc); | 259 | rb.Push(rc); |
| 224 | rb.PushRaw<SecurityParameter>(security_parameter); | 260 | rb.PushRaw<SecurityParameter>(security_parameter); |
| @@ -227,7 +263,7 @@ public: | |||
| 227 | void GetNetworkConfig(Kernel::HLERequestContext& ctx) { | 263 | void GetNetworkConfig(Kernel::HLERequestContext& ctx) { |
| 228 | NetworkConfig config{}; | 264 | NetworkConfig config{}; |
| 229 | NetworkInfo info{}; | 265 | NetworkInfo info{}; |
| 230 | const Result rc = ResultSuccess; | 266 | const Result rc = lan_discovery.GetNetworkInfo(info); |
| 231 | 267 | ||
| 232 | if (rc.IsError()) { | 268 | if (rc.IsError()) { |
| 233 | LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw); | 269 | LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw); |
| @@ -241,12 +277,6 @@ public: | |||
| 241 | config.node_count_max = info.ldn.node_count_max; | 277 | config.node_count_max = info.ldn.node_count_max; |
| 242 | config.local_communication_version = info.ldn.nodes[0].local_communication_version; | 278 | config.local_communication_version = info.ldn.nodes[0].local_communication_version; |
| 243 | 279 | ||
| 244 | LOG_WARNING(Service_LDN, | ||
| 245 | "(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, " | ||
| 246 | "local_communication_version={}", | ||
| 247 | config.intent_id.local_communication_id, config.intent_id.scene_id, | ||
| 248 | config.channel, config.node_count_max, config.local_communication_version); | ||
| 249 | |||
| 250 | IPC::ResponseBuilder rb{ctx, 10}; | 280 | IPC::ResponseBuilder rb{ctx, 10}; |
| 251 | rb.Push(rc); | 281 | rb.Push(rc); |
| 252 | rb.PushRaw<NetworkConfig>(config); | 282 | rb.PushRaw<NetworkConfig>(config); |
| @@ -265,17 +295,17 @@ public: | |||
| 265 | const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); | 295 | const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate); |
| 266 | 296 | ||
| 267 | if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { | 297 | if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) { |
| 268 | LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size, | 298 | LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size, |
| 269 | node_buffer_count); | 299 | node_buffer_count); |
| 270 | IPC::ResponseBuilder rb{ctx, 2}; | 300 | IPC::ResponseBuilder rb{ctx, 2}; |
| 271 | rb.Push(ResultBadInput); | 301 | rb.Push(ResultBadInput); |
| 272 | return; | 302 | return; |
| 273 | } | 303 | } |
| 274 | 304 | ||
| 275 | NetworkInfo info; | 305 | NetworkInfo info{}; |
| 276 | std::vector<NodeLatestUpdate> latest_update(node_buffer_count); | 306 | std::vector<NodeLatestUpdate> latest_update(node_buffer_count); |
| 277 | 307 | ||
| 278 | const auto rc = ResultSuccess; | 308 | const auto rc = lan_discovery.GetNetworkInfo(info, latest_update, latest_update.size()); |
| 279 | if (rc.IsError()) { | 309 | if (rc.IsError()) { |
| 280 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); | 310 | LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw); |
| 281 | IPC::ResponseBuilder rb{ctx, 2}; | 311 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -283,9 +313,6 @@ public: | |||
| 283 | return; | 313 | return; |
| 284 | } | 314 | } |
| 285 | 315 | ||
| 286 | LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}", | ||
| 287 | info.common.ssid.GetStringValue(), info.ldn.node_count); | ||
| 288 | |||
| 289 | ctx.WriteBuffer(info, 0); | 316 | ctx.WriteBuffer(info, 0); |
| 290 | ctx.WriteBuffer(latest_update, 1); | 317 | ctx.WriteBuffer(latest_update, 1); |
| 291 | 318 | ||
| @@ -317,92 +344,78 @@ public: | |||
| 317 | 344 | ||
| 318 | u16 count = 0; | 345 | u16 count = 0; |
| 319 | std::vector<NetworkInfo> network_infos(network_info_size); | 346 | std::vector<NetworkInfo> network_infos(network_info_size); |
| 347 | Result rc = lan_discovery.Scan(network_infos, count, scan_filter); | ||
| 320 | 348 | ||
| 321 | LOG_WARNING(Service_LDN, | 349 | LOG_INFO(Service_LDN, |
| 322 | "(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}", | 350 | "called, channel={}, filter_scan_flag={}, filter_network_type={}, is_private={}", |
| 323 | channel, scan_filter.flag, scan_filter.network_type); | 351 | channel, scan_filter.flag, scan_filter.network_type, is_private); |
| 324 | 352 | ||
| 325 | ctx.WriteBuffer(network_infos); | 353 | ctx.WriteBuffer(network_infos); |
| 326 | 354 | ||
| 327 | IPC::ResponseBuilder rb{ctx, 3}; | 355 | IPC::ResponseBuilder rb{ctx, 3}; |
| 328 | rb.Push(ResultSuccess); | 356 | rb.Push(rc); |
| 329 | rb.Push<u32>(count); | 357 | rb.Push<u32>(count); |
| 330 | } | 358 | } |
| 331 | 359 | ||
| 332 | void OpenAccessPoint(Kernel::HLERequestContext& ctx) { | 360 | void SetWirelessControllerRestriction(Kernel::HLERequestContext& ctx) { |
| 333 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 361 | LOG_WARNING(Service_LDN, "(STUBBED) called"); |
| 334 | 362 | ||
| 335 | IPC::ResponseBuilder rb{ctx, 2}; | 363 | IPC::ResponseBuilder rb{ctx, 2}; |
| 336 | rb.Push(ResultSuccess); | 364 | rb.Push(ResultSuccess); |
| 337 | } | 365 | } |
| 338 | 366 | ||
| 367 | void OpenAccessPoint(Kernel::HLERequestContext& ctx) { | ||
| 368 | LOG_INFO(Service_LDN, "called"); | ||
| 369 | |||
| 370 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 371 | rb.Push(lan_discovery.OpenAccessPoint()); | ||
| 372 | } | ||
| 373 | |||
| 339 | void CloseAccessPoint(Kernel::HLERequestContext& ctx) { | 374 | void CloseAccessPoint(Kernel::HLERequestContext& ctx) { |
| 340 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 375 | LOG_INFO(Service_LDN, "called"); |
| 341 | 376 | ||
| 342 | IPC::ResponseBuilder rb{ctx, 2}; | 377 | IPC::ResponseBuilder rb{ctx, 2}; |
| 343 | rb.Push(ResultSuccess); | 378 | rb.Push(lan_discovery.CloseAccessPoint()); |
| 344 | } | 379 | } |
| 345 | 380 | ||
| 346 | void CreateNetwork(Kernel::HLERequestContext& ctx) { | 381 | void CreateNetwork(Kernel::HLERequestContext& ctx) { |
| 347 | IPC::RequestParser rp{ctx}; | 382 | LOG_INFO(Service_LDN, "called"); |
| 348 | struct Parameters { | ||
| 349 | SecurityConfig security_config; | ||
| 350 | UserConfig user_config; | ||
| 351 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 352 | NetworkConfig network_config; | ||
| 353 | }; | ||
| 354 | static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size."); | ||
| 355 | 383 | ||
| 356 | const auto parameters{rp.PopRaw<Parameters>()}; | 384 | CreateNetworkImpl(ctx); |
| 385 | } | ||
| 357 | 386 | ||
| 358 | LOG_WARNING(Service_LDN, | 387 | void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) { |
| 359 | "(STUBBED) called, passphrase_size={}, security_mode={}, " | 388 | LOG_INFO(Service_LDN, "called"); |
| 360 | "local_communication_version={}", | ||
| 361 | parameters.security_config.passphrase_size, | ||
| 362 | parameters.security_config.security_mode, | ||
| 363 | parameters.network_config.local_communication_version); | ||
| 364 | 389 | ||
| 365 | IPC::ResponseBuilder rb{ctx, 2}; | 390 | CreateNetworkImpl(ctx, true); |
| 366 | rb.Push(ResultSuccess); | ||
| 367 | } | 391 | } |
| 368 | 392 | ||
| 369 | void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) { | 393 | void CreateNetworkImpl(Kernel::HLERequestContext& ctx, bool is_private = false) { |
| 370 | IPC::RequestParser rp{ctx}; | 394 | IPC::RequestParser rp{ctx}; |
| 371 | struct Parameters { | ||
| 372 | SecurityConfig security_config; | ||
| 373 | SecurityParameter security_parameter; | ||
| 374 | UserConfig user_config; | ||
| 375 | NetworkConfig network_config; | ||
| 376 | }; | ||
| 377 | static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size."); | ||
| 378 | |||
| 379 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 380 | 395 | ||
| 381 | LOG_WARNING(Service_LDN, | 396 | const auto security_config{rp.PopRaw<SecurityConfig>()}; |
| 382 | "(STUBBED) called, passphrase_size={}, security_mode={}, " | 397 | [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw<SecurityParameter>() |
| 383 | "local_communication_version={}", | 398 | : SecurityParameter{}}; |
| 384 | parameters.security_config.passphrase_size, | 399 | const auto user_config{rp.PopRaw<UserConfig>()}; |
| 385 | parameters.security_config.security_mode, | 400 | rp.Pop<u32>(); // Padding |
| 386 | parameters.network_config.local_communication_version); | 401 | const auto network_Config{rp.PopRaw<NetworkConfig>()}; |
| 387 | 402 | ||
| 388 | IPC::ResponseBuilder rb{ctx, 2}; | 403 | IPC::ResponseBuilder rb{ctx, 2}; |
| 389 | rb.Push(ResultSuccess); | 404 | rb.Push(lan_discovery.CreateNetwork(security_config, user_config, network_Config)); |
| 390 | } | 405 | } |
| 391 | 406 | ||
| 392 | void DestroyNetwork(Kernel::HLERequestContext& ctx) { | 407 | void DestroyNetwork(Kernel::HLERequestContext& ctx) { |
| 393 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 408 | LOG_INFO(Service_LDN, "called"); |
| 394 | 409 | ||
| 395 | IPC::ResponseBuilder rb{ctx, 2}; | 410 | IPC::ResponseBuilder rb{ctx, 2}; |
| 396 | rb.Push(ResultSuccess); | 411 | rb.Push(lan_discovery.DestroyNetwork()); |
| 397 | } | 412 | } |
| 398 | 413 | ||
| 399 | void SetAdvertiseData(Kernel::HLERequestContext& ctx) { | 414 | void SetAdvertiseData(Kernel::HLERequestContext& ctx) { |
| 400 | std::vector<u8> read_buffer = ctx.ReadBuffer(); | 415 | std::vector<u8> read_buffer = ctx.ReadBuffer(); |
| 401 | 416 | ||
| 402 | LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size()); | ||
| 403 | |||
| 404 | IPC::ResponseBuilder rb{ctx, 2}; | 417 | IPC::ResponseBuilder rb{ctx, 2}; |
| 405 | rb.Push(ResultSuccess); | 418 | rb.Push(lan_discovery.SetAdvertiseData(read_buffer)); |
| 406 | } | 419 | } |
| 407 | 420 | ||
| 408 | void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) { | 421 | void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) { |
| @@ -420,17 +433,17 @@ public: | |||
| 420 | } | 433 | } |
| 421 | 434 | ||
| 422 | void OpenStation(Kernel::HLERequestContext& ctx) { | 435 | void OpenStation(Kernel::HLERequestContext& ctx) { |
| 423 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 436 | LOG_INFO(Service_LDN, "called"); |
| 424 | 437 | ||
| 425 | IPC::ResponseBuilder rb{ctx, 2}; | 438 | IPC::ResponseBuilder rb{ctx, 2}; |
| 426 | rb.Push(ResultSuccess); | 439 | rb.Push(lan_discovery.OpenStation()); |
| 427 | } | 440 | } |
| 428 | 441 | ||
| 429 | void CloseStation(Kernel::HLERequestContext& ctx) { | 442 | void CloseStation(Kernel::HLERequestContext& ctx) { |
| 430 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 443 | LOG_INFO(Service_LDN, "called"); |
| 431 | 444 | ||
| 432 | IPC::ResponseBuilder rb{ctx, 2}; | 445 | IPC::ResponseBuilder rb{ctx, 2}; |
| 433 | rb.Push(ResultSuccess); | 446 | rb.Push(lan_discovery.CloseStation()); |
| 434 | } | 447 | } |
| 435 | 448 | ||
| 436 | void Connect(Kernel::HLERequestContext& ctx) { | 449 | void Connect(Kernel::HLERequestContext& ctx) { |
| @@ -445,16 +458,13 @@ public: | |||
| 445 | 458 | ||
| 446 | const auto parameters{rp.PopRaw<Parameters>()}; | 459 | const auto parameters{rp.PopRaw<Parameters>()}; |
| 447 | 460 | ||
| 448 | LOG_WARNING(Service_LDN, | 461 | LOG_INFO(Service_LDN, |
| 449 | "(STUBBED) called, passphrase_size={}, security_mode={}, " | 462 | "called, passphrase_size={}, security_mode={}, " |
| 450 | "local_communication_version={}", | 463 | "local_communication_version={}", |
| 451 | parameters.security_config.passphrase_size, | 464 | parameters.security_config.passphrase_size, |
| 452 | parameters.security_config.security_mode, | 465 | parameters.security_config.security_mode, parameters.local_communication_version); |
| 453 | parameters.local_communication_version); | ||
| 454 | 466 | ||
| 455 | const std::vector<u8> read_buffer = ctx.ReadBuffer(); | 467 | const std::vector<u8> read_buffer = ctx.ReadBuffer(); |
| 456 | NetworkInfo network_info{}; | ||
| 457 | |||
| 458 | if (read_buffer.size() != sizeof(NetworkInfo)) { | 468 | if (read_buffer.size() != sizeof(NetworkInfo)) { |
| 459 | LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); | 469 | LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); |
| 460 | IPC::ResponseBuilder rb{ctx, 2}; | 470 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -462,40 +472,47 @@ public: | |||
| 462 | return; | 472 | return; |
| 463 | } | 473 | } |
| 464 | 474 | ||
| 475 | NetworkInfo network_info{}; | ||
| 465 | std::memcpy(&network_info, read_buffer.data(), read_buffer.size()); | 476 | std::memcpy(&network_info, read_buffer.data(), read_buffer.size()); |
| 466 | 477 | ||
| 467 | IPC::ResponseBuilder rb{ctx, 2}; | 478 | IPC::ResponseBuilder rb{ctx, 2}; |
| 468 | rb.Push(ResultSuccess); | 479 | rb.Push(lan_discovery.Connect(network_info, parameters.user_config, |
| 480 | static_cast<u16>(parameters.local_communication_version))); | ||
| 469 | } | 481 | } |
| 470 | 482 | ||
| 471 | void Disconnect(Kernel::HLERequestContext& ctx) { | 483 | void Disconnect(Kernel::HLERequestContext& ctx) { |
| 472 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 484 | LOG_INFO(Service_LDN, "called"); |
| 473 | 485 | ||
| 474 | IPC::ResponseBuilder rb{ctx, 2}; | 486 | IPC::ResponseBuilder rb{ctx, 2}; |
| 475 | rb.Push(ResultSuccess); | 487 | rb.Push(lan_discovery.Disconnect()); |
| 476 | } | 488 | } |
| 477 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 478 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 479 | 489 | ||
| 490 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 480 | const auto rc = InitializeImpl(ctx); | 491 | const auto rc = InitializeImpl(ctx); |
| 492 | if (rc.IsError()) { | ||
| 493 | LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); | ||
| 494 | } | ||
| 481 | 495 | ||
| 482 | IPC::ResponseBuilder rb{ctx, 2}; | 496 | IPC::ResponseBuilder rb{ctx, 2}; |
| 483 | rb.Push(rc); | 497 | rb.Push(rc); |
| 484 | } | 498 | } |
| 485 | 499 | ||
| 486 | void Finalize(Kernel::HLERequestContext& ctx) { | 500 | void Finalize(Kernel::HLERequestContext& ctx) { |
| 487 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | 501 | if (auto room_member = room_network.GetRoomMember().lock()) { |
| 502 | room_member->Unbind(ldn_packet_received); | ||
| 503 | } | ||
| 488 | 504 | ||
| 489 | is_initialized = false; | 505 | is_initialized = false; |
| 490 | 506 | ||
| 491 | IPC::ResponseBuilder rb{ctx, 2}; | 507 | IPC::ResponseBuilder rb{ctx, 2}; |
| 492 | rb.Push(ResultSuccess); | 508 | rb.Push(lan_discovery.Finalize()); |
| 493 | } | 509 | } |
| 494 | 510 | ||
| 495 | void Initialize2(Kernel::HLERequestContext& ctx) { | 511 | void Initialize2(Kernel::HLERequestContext& ctx) { |
| 496 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 497 | |||
| 498 | const auto rc = InitializeImpl(ctx); | 512 | const auto rc = InitializeImpl(ctx); |
| 513 | if (rc.IsError()) { | ||
| 514 | LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw); | ||
| 515 | } | ||
| 499 | 516 | ||
| 500 | IPC::ResponseBuilder rb{ctx, 2}; | 517 | IPC::ResponseBuilder rb{ctx, 2}; |
| 501 | rb.Push(rc); | 518 | rb.Push(rc); |
| @@ -508,14 +525,26 @@ public: | |||
| 508 | return ResultAirplaneModeEnabled; | 525 | return ResultAirplaneModeEnabled; |
| 509 | } | 526 | } |
| 510 | 527 | ||
| 528 | if (auto room_member = room_network.GetRoomMember().lock()) { | ||
| 529 | ldn_packet_received = room_member->BindOnLdnPacketReceived( | ||
| 530 | [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); }); | ||
| 531 | } else { | ||
| 532 | LOG_ERROR(Service_LDN, "Couldn't bind callback!"); | ||
| 533 | return ResultAirplaneModeEnabled; | ||
| 534 | } | ||
| 535 | |||
| 536 | lan_discovery.Initialize([&]() { OnEventFired(); }); | ||
| 511 | is_initialized = true; | 537 | is_initialized = true; |
| 512 | // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented | 538 | return ResultSuccess; |
| 513 | return ResultAirplaneModeEnabled; | ||
| 514 | } | 539 | } |
| 515 | 540 | ||
| 516 | KernelHelpers::ServiceContext service_context; | 541 | KernelHelpers::ServiceContext service_context; |
| 517 | Kernel::KEvent* state_change_event; | 542 | Kernel::KEvent* state_change_event; |
| 518 | Network::RoomNetwork& room_network; | 543 | Network::RoomNetwork& room_network; |
| 544 | LANDiscovery lan_discovery; | ||
| 545 | |||
| 546 | // Callback identifier for the OnLDNPacketReceived event. | ||
| 547 | Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received; | ||
| 519 | 548 | ||
| 520 | bool is_initialized{}; | 549 | bool is_initialized{}; |
| 521 | }; | 550 | }; |
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h index 6231e936d..44c2c773b 100644 --- a/src/core/hle/service/ldn/ldn_types.h +++ b/src/core/hle/service/ldn/ldn_types.h | |||
| @@ -31,6 +31,8 @@ enum class NodeStateChange : u8 { | |||
| 31 | DisconnectAndConnect, | 31 | DisconnectAndConnect, |
| 32 | }; | 32 | }; |
| 33 | 33 | ||
| 34 | DECLARE_ENUM_FLAG_OPERATORS(NodeStateChange) | ||
| 35 | |||
| 34 | enum class ScanFilterFlag : u32 { | 36 | enum class ScanFilterFlag : u32 { |
| 35 | None = 0, | 37 | None = 0, |
| 36 | LocalCommunicationId = 1 << 0, | 38 | LocalCommunicationId = 1 << 0, |
| @@ -100,13 +102,13 @@ enum class AcceptPolicy : u8 { | |||
| 100 | 102 | ||
| 101 | enum class WifiChannel : s16 { | 103 | enum class WifiChannel : s16 { |
| 102 | Default = 0, | 104 | Default = 0, |
| 103 | wifi24_1 = 1, | 105 | Wifi24_1 = 1, |
| 104 | wifi24_6 = 6, | 106 | Wifi24_6 = 6, |
| 105 | wifi24_11 = 11, | 107 | Wifi24_11 = 11, |
| 106 | wifi50_36 = 36, | 108 | Wifi50_36 = 36, |
| 107 | wifi50_40 = 40, | 109 | Wifi50_40 = 40, |
| 108 | wifi50_44 = 44, | 110 | Wifi50_44 = 44, |
| 109 | wifi50_48 = 48, | 111 | Wifi50_48 = 48, |
| 110 | }; | 112 | }; |
| 111 | 113 | ||
| 112 | enum class LinkLevel : s8 { | 114 | enum class LinkLevel : s8 { |
| @@ -116,6 +118,11 @@ enum class LinkLevel : s8 { | |||
| 116 | Excellent, | 118 | Excellent, |
| 117 | }; | 119 | }; |
| 118 | 120 | ||
| 121 | enum class NodeStatus : u8 { | ||
| 122 | Disconnected, | ||
| 123 | Connected, | ||
| 124 | }; | ||
| 125 | |||
| 119 | struct NodeLatestUpdate { | 126 | struct NodeLatestUpdate { |
| 120 | NodeStateChange state_change; | 127 | NodeStateChange state_change; |
| 121 | INSERT_PADDING_BYTES(0x7); // Unknown | 128 | INSERT_PADDING_BYTES(0x7); // Unknown |
| @@ -150,7 +157,7 @@ struct Ssid { | |||
| 150 | 157 | ||
| 151 | Ssid() = default; | 158 | Ssid() = default; |
| 152 | 159 | ||
| 153 | explicit Ssid(std::string_view data) { | 160 | constexpr explicit Ssid(std::string_view data) { |
| 154 | length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); | 161 | length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); |
| 155 | data.copy(raw.data(), length); | 162 | data.copy(raw.data(), length); |
| 156 | raw[length] = 0; | 163 | raw[length] = 0; |
| @@ -159,19 +166,18 @@ struct Ssid { | |||
| 159 | std::string GetStringValue() const { | 166 | std::string GetStringValue() const { |
| 160 | return std::string(raw.data()); | 167 | return std::string(raw.data()); |
| 161 | } | 168 | } |
| 162 | }; | ||
| 163 | static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size"); | ||
| 164 | 169 | ||
| 165 | struct Ipv4Address { | 170 | bool operator==(const Ssid& b) const { |
| 166 | union { | 171 | return (length == b.length) && (std::memcmp(raw.data(), b.raw.data(), length) == 0); |
| 167 | u32 raw{}; | 172 | } |
| 168 | std::array<u8, 4> bytes; | ||
| 169 | }; | ||
| 170 | 173 | ||
| 171 | std::string GetStringValue() const { | 174 | bool operator!=(const Ssid& b) const { |
| 172 | return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); | 175 | return !operator==(b); |
| 173 | } | 176 | } |
| 174 | }; | 177 | }; |
| 178 | static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size"); | ||
| 179 | |||
| 180 | using Ipv4Address = std::array<u8, 4>; | ||
| 175 | static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size"); | 181 | static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size"); |
| 176 | 182 | ||
| 177 | struct MacAddress { | 183 | struct MacAddress { |
| @@ -181,6 +187,14 @@ struct MacAddress { | |||
| 181 | }; | 187 | }; |
| 182 | static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size"); | 188 | static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size"); |
| 183 | 189 | ||
| 190 | struct MACAddressHash { | ||
| 191 | size_t operator()(const MacAddress& address) const { | ||
| 192 | u64 value{}; | ||
| 193 | std::memcpy(&value, address.raw.data(), sizeof(address.raw)); | ||
| 194 | return value; | ||
| 195 | } | ||
| 196 | }; | ||
| 197 | |||
| 184 | struct ScanFilter { | 198 | struct ScanFilter { |
| 185 | NetworkId network_id; | 199 | NetworkId network_id; |
| 186 | NetworkType network_type; | 200 | NetworkType network_type; |
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index c484a9c8d..3a2fe938f 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp | |||
| @@ -427,12 +427,11 @@ CharInfo MiiManager::BuildDefault(std::size_t index) { | |||
| 427 | return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); | 427 | return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); |
| 428 | } | 428 | } |
| 429 | 429 | ||
| 430 | CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { | 430 | CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { |
| 431 | Service::Mii::MiiManager manager; | 431 | Service::Mii::MiiManager manager; |
| 432 | auto mii = manager.BuildDefault(0); | 432 | auto mii = manager.BuildDefault(0); |
| 433 | 433 | ||
| 434 | // Check if mii data exist | 434 | if (!ValidateV3Info(mii_v3)) { |
| 435 | if (mii_v3.mii_name[0] == 0) { | ||
| 436 | return mii; | 435 | return mii; |
| 437 | } | 436 | } |
| 438 | 437 | ||
| @@ -443,8 +442,15 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { | |||
| 443 | mii.height = mii_v3.height; | 442 | mii.height = mii_v3.height; |
| 444 | mii.build = mii_v3.build; | 443 | mii.build = mii_v3.build; |
| 445 | 444 | ||
| 446 | memset(mii.name.data(), 0, sizeof(mii.name)); | 445 | // Copy name until string terminator |
| 447 | memcpy(mii.name.data(), mii_v3.mii_name.data(), sizeof(mii_v3.mii_name)); | 446 | mii.name = {}; |
| 447 | for (std::size_t index = 0; index < mii.name.size() - 1; index++) { | ||
| 448 | mii.name[index] = mii_v3.mii_name[index]; | ||
| 449 | if (mii.name[index] == 0) { | ||
| 450 | break; | ||
| 451 | } | ||
| 452 | } | ||
| 453 | |||
| 448 | mii.font_region = mii_v3.region_information.character_set; | 454 | mii.font_region = mii_v3.region_information.character_set; |
| 449 | 455 | ||
| 450 | mii.faceline_type = mii_v3.appearance_bits1.face_shape; | 456 | mii.faceline_type = mii_v3.appearance_bits1.face_shape; |
| @@ -504,6 +510,151 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { | |||
| 504 | return mii; | 510 | return mii; |
| 505 | } | 511 | } |
| 506 | 512 | ||
| 513 | Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const { | ||
| 514 | Service::Mii::MiiManager manager; | ||
| 515 | Ver3StoreData mii_v3{}; | ||
| 516 | |||
| 517 | // TODO: We are ignoring a bunch of data from the mii_v3 | ||
| 518 | |||
| 519 | mii_v3.version = 1; | ||
| 520 | mii_v3.mii_information.gender.Assign(mii.gender); | ||
| 521 | mii_v3.mii_information.favorite_color.Assign(mii.favorite_color); | ||
| 522 | mii_v3.height = mii.height; | ||
| 523 | mii_v3.build = mii.build; | ||
| 524 | |||
| 525 | // Copy name until string terminator | ||
| 526 | mii_v3.mii_name = {}; | ||
| 527 | for (std::size_t index = 0; index < mii.name.size() - 1; index++) { | ||
| 528 | mii_v3.mii_name[index] = mii.name[index]; | ||
| 529 | if (mii_v3.mii_name[index] == 0) { | ||
| 530 | break; | ||
| 531 | } | ||
| 532 | } | ||
| 533 | |||
| 534 | mii_v3.region_information.character_set.Assign(mii.font_region); | ||
| 535 | |||
| 536 | mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type); | ||
| 537 | mii_v3.appearance_bits1.skin_color.Assign(mii.faceline_color); | ||
| 538 | mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle); | ||
| 539 | mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make); | ||
| 540 | |||
| 541 | mii_v3.hair_style = mii.hair_type; | ||
| 542 | mii_v3.appearance_bits3.hair_color.Assign(mii.hair_color); | ||
| 543 | mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip); | ||
| 544 | |||
| 545 | mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type); | ||
| 546 | mii_v3.appearance_bits4.eye_color.Assign(mii.eye_color); | ||
| 547 | mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale); | ||
| 548 | mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect); | ||
| 549 | mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate); | ||
| 550 | mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x); | ||
| 551 | mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y); | ||
| 552 | |||
| 553 | mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type); | ||
| 554 | mii_v3.appearance_bits5.eyebrow_color.Assign(mii.eyebrow_color); | ||
| 555 | mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale); | ||
| 556 | mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect); | ||
| 557 | mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate); | ||
| 558 | mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x); | ||
| 559 | mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y); | ||
| 560 | |||
| 561 | mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type); | ||
| 562 | mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale); | ||
| 563 | mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y); | ||
| 564 | |||
| 565 | mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type); | ||
| 566 | mii_v3.appearance_bits7.mouth_color.Assign(mii.mouth_color); | ||
| 567 | mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale); | ||
| 568 | mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect); | ||
| 569 | mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y); | ||
| 570 | |||
| 571 | mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type); | ||
| 572 | mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale); | ||
| 573 | mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y); | ||
| 574 | |||
| 575 | mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type); | ||
| 576 | mii_v3.appearance_bits9.facial_hair_color.Assign(mii.beard_color); | ||
| 577 | |||
| 578 | mii_v3.appearance_bits10.glasses_type.Assign(mii.glasses_type); | ||
| 579 | mii_v3.appearance_bits10.glasses_color.Assign(mii.glasses_color); | ||
| 580 | mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale); | ||
| 581 | mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y); | ||
| 582 | |||
| 583 | mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type); | ||
| 584 | mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale); | ||
| 585 | mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x); | ||
| 586 | mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y); | ||
| 587 | |||
| 588 | // TODO: Validate mii_v3 data | ||
| 589 | |||
| 590 | return mii_v3; | ||
| 591 | } | ||
| 592 | |||
| 593 | bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const { | ||
| 594 | bool is_valid = mii_v3.version == 0 || mii_v3.version == 3; | ||
| 595 | |||
| 596 | is_valid = is_valid && (mii_v3.mii_name[0] != 0); | ||
| 597 | |||
| 598 | is_valid = is_valid && (mii_v3.mii_information.birth_month < 13); | ||
| 599 | is_valid = is_valid && (mii_v3.mii_information.birth_day < 32); | ||
| 600 | is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12); | ||
| 601 | is_valid = is_valid && (mii_v3.height < 128); | ||
| 602 | is_valid = is_valid && (mii_v3.build < 128); | ||
| 603 | |||
| 604 | is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12); | ||
| 605 | is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7); | ||
| 606 | is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12); | ||
| 607 | is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12); | ||
| 608 | |||
| 609 | is_valid = is_valid && (mii_v3.hair_style < 132); | ||
| 610 | is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8); | ||
| 611 | |||
| 612 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60); | ||
| 613 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6); | ||
| 614 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8); | ||
| 615 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7); | ||
| 616 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8); | ||
| 617 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13); | ||
| 618 | is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19); | ||
| 619 | |||
| 620 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25); | ||
| 621 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8); | ||
| 622 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9); | ||
| 623 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7); | ||
| 624 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12); | ||
| 625 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12); | ||
| 626 | is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19); | ||
| 627 | |||
| 628 | is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18); | ||
| 629 | is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9); | ||
| 630 | is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19); | ||
| 631 | |||
| 632 | is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36); | ||
| 633 | is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5); | ||
| 634 | is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9); | ||
| 635 | is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7); | ||
| 636 | is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19); | ||
| 637 | |||
| 638 | is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6); | ||
| 639 | is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7); | ||
| 640 | is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17); | ||
| 641 | |||
| 642 | is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6); | ||
| 643 | is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8); | ||
| 644 | |||
| 645 | is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9); | ||
| 646 | is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6); | ||
| 647 | is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8); | ||
| 648 | is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21); | ||
| 649 | |||
| 650 | is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2); | ||
| 651 | is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9); | ||
| 652 | is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17); | ||
| 653 | is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31); | ||
| 654 | |||
| 655 | return is_valid; | ||
| 656 | } | ||
| 657 | |||
| 507 | ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { | 658 | ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { |
| 508 | std::vector<MiiInfoElement> result; | 659 | std::vector<MiiInfoElement> result; |
| 509 | 660 | ||
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index d847de0bd..83ad3d343 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h | |||
| @@ -22,7 +22,9 @@ public: | |||
| 22 | ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag); | 22 | ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag); |
| 23 | CharInfo BuildRandom(Age age, Gender gender, Race race); | 23 | CharInfo BuildRandom(Age age, Gender gender, Race race); |
| 24 | CharInfo BuildDefault(std::size_t index); | 24 | CharInfo BuildDefault(std::size_t index); |
| 25 | CharInfo ConvertV3ToCharInfo(Ver3StoreData mii_v3) const; | 25 | CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; |
| 26 | Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const; | ||
| 27 | bool ValidateV3Info(const Ver3StoreData& mii_v3) const; | ||
| 26 | ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag); | 28 | ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag); |
| 27 | Result GetIndex(const CharInfo& info, u32& index); | 29 | Result GetIndex(const CharInfo& info, u32& index); |
| 28 | 30 | ||
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 13a843a28..046c5f18f 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp | |||
| @@ -106,10 +106,10 @@ public: | |||
| 106 | {1, &IUser::FinalizeOld, "FinalizeOld"}, | 106 | {1, &IUser::FinalizeOld, "FinalizeOld"}, |
| 107 | {2, &IUser::GetStateOld, "GetStateOld"}, | 107 | {2, &IUser::GetStateOld, "GetStateOld"}, |
| 108 | {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"}, | 108 | {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"}, |
| 109 | {400, nullptr, "Initialize"}, | 109 | {400, &IUser::InitializeOld, "Initialize"}, |
| 110 | {401, nullptr, "Finalize"}, | 110 | {401, &IUser::FinalizeOld, "Finalize"}, |
| 111 | {402, nullptr, "GetState"}, | 111 | {402, &IUser::GetStateOld, "GetState"}, |
| 112 | {403, nullptr, "IsNfcEnabled"}, | 112 | {403, &IUser::IsNfcEnabledOld, "IsNfcEnabled"}, |
| 113 | {404, nullptr, "ListDevices"}, | 113 | {404, nullptr, "ListDevices"}, |
| 114 | {405, nullptr, "GetDeviceState"}, | 114 | {405, nullptr, "GetDeviceState"}, |
| 115 | {406, nullptr, "GetNpadId"}, | 115 | {406, nullptr, "GetNpadId"}, |
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp index 31dd3a307..c32a6816b 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.cpp +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp | |||
| @@ -20,14 +20,15 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { | |||
| 20 | const auto& amiibo_data = ntag_file.user_memory; | 20 | const auto& amiibo_data = ntag_file.user_memory; |
| 21 | LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); | 21 | LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); |
| 22 | LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); | 22 | LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); |
| 23 | LOG_INFO(Service_NFP, "write_count={}", amiibo_data.write_counter); | 23 | LOG_DEBUG(Service_NFP, "write_count={}", static_cast<u16>(amiibo_data.write_counter)); |
| 24 | 24 | ||
| 25 | LOG_INFO(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); | 25 | LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); |
| 26 | LOG_INFO(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); | 26 | LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); |
| 27 | LOG_INFO(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); | 27 | LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); |
| 28 | LOG_INFO(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number); | 28 | LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", |
| 29 | LOG_INFO(Service_NFP, "series={}", amiibo_data.model_info.series); | 29 | static_cast<u16>(amiibo_data.model_info.model_number)); |
| 30 | LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.constant_value); | 30 | LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); |
| 31 | LOG_DEBUG(Service_NFP, "tag_type=0x{0:x}", amiibo_data.model_info.tag_type); | ||
| 31 | 32 | ||
| 32 | LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); | 33 | LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); |
| 33 | LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0); | 34 | LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0); |
| @@ -35,11 +36,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { | |||
| 35 | 36 | ||
| 36 | // Validate UUID | 37 | // Validate UUID |
| 37 | constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` | 38 | constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3` |
| 38 | if ((CT ^ ntag_file.uuid[0] ^ ntag_file.uuid[1] ^ ntag_file.uuid[2]) != ntag_file.uuid[3]) { | 39 | if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) != |
| 40 | ntag_file.uuid.uid[3]) { | ||
| 39 | return false; | 41 | return false; |
| 40 | } | 42 | } |
| 41 | if ((ntag_file.uuid[4] ^ ntag_file.uuid[5] ^ ntag_file.uuid[6] ^ ntag_file.uuid[7]) != | 43 | if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^ |
| 42 | ntag_file.uuid[8]) { | 44 | ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) { |
| 43 | return false; | 45 | return false; |
| 44 | } | 46 | } |
| 45 | 47 | ||
| @@ -53,11 +55,12 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { | |||
| 53 | if (amiibo_data.constant_value != 0xA5) { | 55 | if (amiibo_data.constant_value != 0xA5) { |
| 54 | return false; | 56 | return false; |
| 55 | } | 57 | } |
| 56 | if (amiibo_data.model_info.constant_value != 0x02) { | 58 | if (amiibo_data.model_info.tag_type != PackedTagType::Type2) { |
| 59 | return false; | ||
| 60 | } | ||
| 61 | if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) { | ||
| 57 | return false; | 62 | return false; |
| 58 | } | 63 | } |
| 59 | // dynamic_lock value apparently is not constant | ||
| 60 | // ntag_file.dynamic_lock == 0x0F0001 | ||
| 61 | if (ntag_file.CFG0 != 0x04000000U) { | 64 | if (ntag_file.CFG0 != 0x04000000U) { |
| 62 | return false; | 65 | return false; |
| 63 | } | 66 | } |
| @@ -70,7 +73,8 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { | |||
| 70 | NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { | 73 | NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { |
| 71 | NTAG215File encoded_data{}; | 74 | NTAG215File encoded_data{}; |
| 72 | 75 | ||
| 73 | memcpy(encoded_data.uuid2.data(), nfc_data.uuid.data() + 0x8, sizeof(encoded_data.uuid2)); | 76 | encoded_data.uid = nfc_data.uuid.uid; |
| 77 | encoded_data.nintendo_id = nfc_data.uuid.nintendo_id; | ||
| 74 | encoded_data.static_lock = nfc_data.static_lock; | 78 | encoded_data.static_lock = nfc_data.static_lock; |
| 75 | encoded_data.compability_container = nfc_data.compability_container; | 79 | encoded_data.compability_container = nfc_data.compability_container; |
| 76 | encoded_data.hmac_data = nfc_data.user_memory.hmac_data; | 80 | encoded_data.hmac_data = nfc_data.user_memory.hmac_data; |
| @@ -82,10 +86,10 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { | |||
| 82 | encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter; | 86 | encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter; |
| 83 | encoded_data.application_area_id = nfc_data.user_memory.application_area_id; | 87 | encoded_data.application_area_id = nfc_data.user_memory.application_area_id; |
| 84 | encoded_data.unknown = nfc_data.user_memory.unknown; | 88 | encoded_data.unknown = nfc_data.user_memory.unknown; |
| 85 | encoded_data.hash = nfc_data.user_memory.hash; | 89 | encoded_data.unknown2 = nfc_data.user_memory.unknown2; |
| 86 | encoded_data.application_area = nfc_data.user_memory.application_area; | 90 | encoded_data.application_area = nfc_data.user_memory.application_area; |
| 87 | encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; | 91 | encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag; |
| 88 | memcpy(encoded_data.uuid.data(), nfc_data.uuid.data(), sizeof(encoded_data.uuid)); | 92 | encoded_data.lock_bytes = nfc_data.uuid.lock_bytes; |
| 89 | encoded_data.model_info = nfc_data.user_memory.model_info; | 93 | encoded_data.model_info = nfc_data.user_memory.model_info; |
| 90 | encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; | 94 | encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt; |
| 91 | encoded_data.dynamic_lock = nfc_data.dynamic_lock; | 95 | encoded_data.dynamic_lock = nfc_data.dynamic_lock; |
| @@ -99,8 +103,9 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { | |||
| 99 | EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { | 103 | EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { |
| 100 | EncryptedNTAG215File nfc_data{}; | 104 | EncryptedNTAG215File nfc_data{}; |
| 101 | 105 | ||
| 102 | memcpy(nfc_data.uuid.data() + 0x8, encoded_data.uuid2.data(), sizeof(encoded_data.uuid2)); | 106 | nfc_data.uuid.uid = encoded_data.uid; |
| 103 | memcpy(nfc_data.uuid.data(), encoded_data.uuid.data(), sizeof(encoded_data.uuid)); | 107 | nfc_data.uuid.nintendo_id = encoded_data.nintendo_id; |
| 108 | nfc_data.uuid.lock_bytes = encoded_data.lock_bytes; | ||
| 104 | nfc_data.static_lock = encoded_data.static_lock; | 109 | nfc_data.static_lock = encoded_data.static_lock; |
| 105 | nfc_data.compability_container = encoded_data.compability_container; | 110 | nfc_data.compability_container = encoded_data.compability_container; |
| 106 | nfc_data.user_memory.hmac_data = encoded_data.hmac_data; | 111 | nfc_data.user_memory.hmac_data = encoded_data.hmac_data; |
| @@ -112,7 +117,7 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { | |||
| 112 | nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter; | 117 | nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter; |
| 113 | nfc_data.user_memory.application_area_id = encoded_data.application_area_id; | 118 | nfc_data.user_memory.application_area_id = encoded_data.application_area_id; |
| 114 | nfc_data.user_memory.unknown = encoded_data.unknown; | 119 | nfc_data.user_memory.unknown = encoded_data.unknown; |
| 115 | nfc_data.user_memory.hash = encoded_data.hash; | 120 | nfc_data.user_memory.unknown2 = encoded_data.unknown2; |
| 116 | nfc_data.user_memory.application_area = encoded_data.application_area; | 121 | nfc_data.user_memory.application_area = encoded_data.application_area; |
| 117 | nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag; | 122 | nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag; |
| 118 | nfc_data.user_memory.model_info = encoded_data.model_info; | 123 | nfc_data.user_memory.model_info = encoded_data.model_info; |
| @@ -127,10 +132,10 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { | |||
| 127 | 132 | ||
| 128 | u32 GetTagPassword(const TagUuid& uuid) { | 133 | u32 GetTagPassword(const TagUuid& uuid) { |
| 129 | // Verifiy that the generated password is correct | 134 | // Verifiy that the generated password is correct |
| 130 | u32 password = 0xAA ^ (uuid[1] ^ uuid[3]); | 135 | u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]); |
| 131 | password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8; | 136 | password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8; |
| 132 | password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16; | 137 | password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16; |
| 133 | password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24; | 138 | password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24; |
| 134 | return password; | 139 | return password; |
| 135 | } | 140 | } |
| 136 | 141 | ||
| @@ -138,15 +143,13 @@ HashSeed GetSeed(const NTAG215File& data) { | |||
| 138 | HashSeed seed{ | 143 | HashSeed seed{ |
| 139 | .magic = data.write_counter, | 144 | .magic = data.write_counter, |
| 140 | .padding = {}, | 145 | .padding = {}, |
| 141 | .uuid1 = {}, | 146 | .uid_1 = data.uid, |
| 142 | .uuid2 = {}, | 147 | .nintendo_id_1 = data.nintendo_id, |
| 148 | .uid_2 = data.uid, | ||
| 149 | .nintendo_id_2 = data.nintendo_id, | ||
| 143 | .keygen_salt = data.keygen_salt, | 150 | .keygen_salt = data.keygen_salt, |
| 144 | }; | 151 | }; |
| 145 | 152 | ||
| 146 | // Copy the first 8 bytes of uuid | ||
| 147 | memcpy(seed.uuid1.data(), data.uuid.data(), sizeof(seed.uuid1)); | ||
| 148 | memcpy(seed.uuid2.data(), data.uuid.data(), sizeof(seed.uuid2)); | ||
| 149 | |||
| 150 | return seed; | 153 | return seed; |
| 151 | } | 154 | } |
| 152 | 155 | ||
| @@ -165,8 +168,10 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed | |||
| 165 | output.insert(output.end(), key.magic_bytes.begin(), | 168 | output.insert(output.end(), key.magic_bytes.begin(), |
| 166 | key.magic_bytes.begin() + key.magic_length); | 169 | key.magic_bytes.begin() + key.magic_length); |
| 167 | 170 | ||
| 168 | output.insert(output.end(), seed.uuid1.begin(), seed.uuid1.end()); | 171 | output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end()); |
| 169 | output.insert(output.end(), seed.uuid2.begin(), seed.uuid2.end()); | 172 | output.emplace_back(seed.nintendo_id_1); |
| 173 | output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end()); | ||
| 174 | output.emplace_back(seed.nintendo_id_2); | ||
| 170 | 175 | ||
| 171 | for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { | 176 | for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) { |
| 172 | output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i])); | 177 | output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i])); |
| @@ -177,7 +182,6 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed | |||
| 177 | 182 | ||
| 178 | void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, | 183 | void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, |
| 179 | const std::vector<u8>& seed) { | 184 | const std::vector<u8>& seed) { |
| 180 | |||
| 181 | // Initialize context | 185 | // Initialize context |
| 182 | ctx.used = false; | 186 | ctx.used = false; |
| 183 | ctx.counter = 0; | 187 | ctx.counter = 0; |
| @@ -250,14 +254,15 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou | |||
| 250 | reinterpret_cast<unsigned char*>(&out_data.settings)); | 254 | reinterpret_cast<unsigned char*>(&out_data.settings)); |
| 251 | 255 | ||
| 252 | // Copy the rest of the data directly | 256 | // Copy the rest of the data directly |
| 253 | out_data.uuid2 = in_data.uuid2; | 257 | out_data.uid = in_data.uid; |
| 258 | out_data.nintendo_id = in_data.nintendo_id; | ||
| 259 | out_data.lock_bytes = in_data.lock_bytes; | ||
| 254 | out_data.static_lock = in_data.static_lock; | 260 | out_data.static_lock = in_data.static_lock; |
| 255 | out_data.compability_container = in_data.compability_container; | 261 | out_data.compability_container = in_data.compability_container; |
| 256 | 262 | ||
| 257 | out_data.constant_value = in_data.constant_value; | 263 | out_data.constant_value = in_data.constant_value; |
| 258 | out_data.write_counter = in_data.write_counter; | 264 | out_data.write_counter = in_data.write_counter; |
| 259 | 265 | ||
| 260 | out_data.uuid = in_data.uuid; | ||
| 261 | out_data.model_info = in_data.model_info; | 266 | out_data.model_info = in_data.model_info; |
| 262 | out_data.keygen_salt = in_data.keygen_salt; | 267 | out_data.keygen_salt = in_data.keygen_salt; |
| 263 | out_data.dynamic_lock = in_data.dynamic_lock; | 268 | out_data.dynamic_lock = in_data.dynamic_lock; |
| @@ -309,7 +314,7 @@ bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& t | |||
| 309 | // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC! | 314 | // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC! |
| 310 | constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; | 315 | constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; |
| 311 | mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), | 316 | mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), |
| 312 | sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uuid), | 317 | sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid), |
| 313 | input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag)); | 318 | input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag)); |
| 314 | 319 | ||
| 315 | // Regenerate data HMAC | 320 | // Regenerate data HMAC |
| @@ -350,7 +355,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t | |||
| 350 | constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; | 355 | constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START; |
| 351 | constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START; | 356 | constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START; |
| 352 | mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), | 357 | mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(), |
| 353 | sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uuid), | 358 | sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid), |
| 354 | input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag)); | 359 | input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag)); |
| 355 | 360 | ||
| 356 | // Init mbedtls HMAC context | 361 | // Init mbedtls HMAC context |
| @@ -364,7 +369,7 @@ bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_t | |||
| 364 | input_length2); // Data | 369 | input_length2); // Data |
| 365 | mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag), | 370 | mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag), |
| 366 | sizeof(HashData)); // Tag HMAC | 371 | sizeof(HashData)); // Tag HMAC |
| 367 | mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uuid), | 372 | mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uid), |
| 368 | input_length); | 373 | input_length); |
| 369 | mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data)); | 374 | mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data)); |
| 370 | 375 | ||
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h index af7335912..0175ced91 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.h +++ b/src/core/hle/service/nfp/amiibo_crypto.h | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | 7 | ||
| 8 | #include "core/hle/service/nfp/amiibo_types.h" | 8 | #include "core/hle/service/nfp/nfp_types.h" |
| 9 | 9 | ||
| 10 | struct mbedtls_md_context_t; | 10 | struct mbedtls_md_context_t; |
| 11 | 11 | ||
| @@ -22,10 +22,12 @@ using HmacKey = std::array<u8, 0x10>; | |||
| 22 | using DrgbOutput = std::array<u8, 0x20>; | 22 | using DrgbOutput = std::array<u8, 0x20>; |
| 23 | 23 | ||
| 24 | struct HashSeed { | 24 | struct HashSeed { |
| 25 | u16 magic; | 25 | u16_be magic; |
| 26 | std::array<u8, 0xE> padding; | 26 | std::array<u8, 0xE> padding; |
| 27 | std::array<u8, 0x8> uuid1; | 27 | UniqueSerialNumber uid_1; |
| 28 | std::array<u8, 0x8> uuid2; | 28 | u8 nintendo_id_1; |
| 29 | UniqueSerialNumber uid_2; | ||
| 30 | u8 nintendo_id_2; | ||
| 29 | std::array<u8, 0x20> keygen_salt; | 31 | std::array<u8, 0x20> keygen_salt; |
| 30 | }; | 32 | }; |
| 31 | static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); | 33 | static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size"); |
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index e0ed3f771..0cb55ca49 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -1,1098 +1,43 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <array> | ||
| 5 | #include <atomic> | ||
| 6 | |||
| 7 | #include "common/fs/file.h" | ||
| 8 | #include "common/fs/path_util.h" | ||
| 9 | #include "common/logging/log.h" | 4 | #include "common/logging/log.h" |
| 10 | #include "common/string_util.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/hid/emulated_controller.h" | ||
| 13 | #include "core/hid/hid_core.h" | ||
| 14 | #include "core/hid/hid_types.h" | ||
| 15 | #include "core/hle/ipc_helpers.h" | 5 | #include "core/hle/ipc_helpers.h" |
| 16 | #include "core/hle/kernel/k_event.h" | ||
| 17 | #include "core/hle/service/mii/mii_manager.h" | ||
| 18 | #include "core/hle/service/nfp/amiibo_crypto.h" | ||
| 19 | #include "core/hle/service/nfp/nfp.h" | 6 | #include "core/hle/service/nfp/nfp.h" |
| 20 | #include "core/hle/service/nfp/nfp_user.h" | 7 | #include "core/hle/service/nfp/nfp_user.h" |
| 21 | 8 | ||
| 22 | namespace Service::NFP { | 9 | namespace Service::NFP { |
| 23 | namespace ErrCodes { | ||
| 24 | constexpr Result DeviceNotFound(ErrorModule::NFP, 64); | ||
| 25 | constexpr Result WrongDeviceState(ErrorModule::NFP, 73); | ||
| 26 | constexpr Result NfcDisabled(ErrorModule::NFP, 80); | ||
| 27 | constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); | ||
| 28 | constexpr Result TagRemoved(ErrorModule::NFP, 97); | ||
| 29 | constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); | ||
| 30 | constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); | ||
| 31 | constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); | ||
| 32 | } // namespace ErrCodes | ||
| 33 | |||
| 34 | IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_) | ||
| 35 | : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name}, | ||
| 36 | nfp_interface{nfp_interface_} { | ||
| 37 | static const FunctionInfo functions[] = { | ||
| 38 | {0, &IUser::Initialize, "Initialize"}, | ||
| 39 | {1, &IUser::Finalize, "Finalize"}, | ||
| 40 | {2, &IUser::ListDevices, "ListDevices"}, | ||
| 41 | {3, &IUser::StartDetection, "StartDetection"}, | ||
| 42 | {4, &IUser::StopDetection, "StopDetection"}, | ||
| 43 | {5, &IUser::Mount, "Mount"}, | ||
| 44 | {6, &IUser::Unmount, "Unmount"}, | ||
| 45 | {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, | ||
| 46 | {8, &IUser::GetApplicationArea, "GetApplicationArea"}, | ||
| 47 | {9, &IUser::SetApplicationArea, "SetApplicationArea"}, | ||
| 48 | {10, &IUser::Flush, "Flush"}, | ||
| 49 | {11, nullptr, "Restore"}, | ||
| 50 | {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, | ||
| 51 | {13, &IUser::GetTagInfo, "GetTagInfo"}, | ||
| 52 | {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, | ||
| 53 | {15, &IUser::GetCommonInfo, "GetCommonInfo"}, | ||
| 54 | {16, &IUser::GetModelInfo, "GetModelInfo"}, | ||
| 55 | {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, | ||
| 56 | {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, | ||
| 57 | {19, &IUser::GetState, "GetState"}, | ||
| 58 | {20, &IUser::GetDeviceState, "GetDeviceState"}, | ||
| 59 | {21, &IUser::GetNpadId, "GetNpadId"}, | ||
| 60 | {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, | ||
| 61 | {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, | ||
| 62 | {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"}, | ||
| 63 | }; | ||
| 64 | RegisterHandlers(functions); | ||
| 65 | |||
| 66 | availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); | ||
| 67 | } | ||
| 68 | |||
| 69 | void IUser::Initialize(Kernel::HLERequestContext& ctx) { | ||
| 70 | LOG_INFO(Service_NFC, "called"); | ||
| 71 | |||
| 72 | state = State::Initialized; | ||
| 73 | |||
| 74 | // TODO(german77): Loop through all interfaces | ||
| 75 | nfp_interface.Initialize(); | ||
| 76 | |||
| 77 | IPC::ResponseBuilder rb{ctx, 2, 0}; | ||
| 78 | rb.Push(ResultSuccess); | ||
| 79 | } | ||
| 80 | |||
| 81 | void IUser::Finalize(Kernel::HLERequestContext& ctx) { | ||
| 82 | LOG_INFO(Service_NFP, "called"); | ||
| 83 | |||
| 84 | state = State::NonInitialized; | ||
| 85 | |||
| 86 | // TODO(german77): Loop through all interfaces | ||
| 87 | nfp_interface.Finalize(); | ||
| 88 | |||
| 89 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 90 | rb.Push(ResultSuccess); | ||
| 91 | } | ||
| 92 | |||
| 93 | void IUser::ListDevices(Kernel::HLERequestContext& ctx) { | ||
| 94 | LOG_INFO(Service_NFP, "called"); | ||
| 95 | |||
| 96 | if (state == State::NonInitialized) { | ||
| 97 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 98 | rb.Push(ErrCodes::NfcDisabled); | ||
| 99 | return; | ||
| 100 | } | ||
| 101 | |||
| 102 | std::vector<u64> devices; | ||
| 103 | |||
| 104 | // TODO(german77): Loop through all interfaces | ||
| 105 | devices.push_back(nfp_interface.GetHandle()); | ||
| 106 | |||
| 107 | if (devices.size() == 0) { | ||
| 108 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 109 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 110 | return; | ||
| 111 | } | ||
| 112 | |||
| 113 | ctx.WriteBuffer(devices); | ||
| 114 | |||
| 115 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 116 | rb.Push(ResultSuccess); | ||
| 117 | rb.Push(static_cast<s32>(devices.size())); | ||
| 118 | } | ||
| 119 | |||
| 120 | void IUser::StartDetection(Kernel::HLERequestContext& ctx) { | ||
| 121 | IPC::RequestParser rp{ctx}; | ||
| 122 | const auto device_handle{rp.Pop<u64>()}; | ||
| 123 | const auto nfp_protocol{rp.Pop<s32>()}; | ||
| 124 | LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); | ||
| 125 | |||
| 126 | if (state == State::NonInitialized) { | ||
| 127 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 128 | rb.Push(ErrCodes::NfcDisabled); | ||
| 129 | return; | ||
| 130 | } | ||
| 131 | |||
| 132 | // TODO(german77): Loop through all interfaces | ||
| 133 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 134 | const auto result = nfp_interface.StartDetection(nfp_protocol); | ||
| 135 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 136 | rb.Push(result); | ||
| 137 | return; | ||
| 138 | } | ||
| 139 | |||
| 140 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 141 | |||
| 142 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 143 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 144 | } | ||
| 145 | |||
| 146 | void IUser::StopDetection(Kernel::HLERequestContext& ctx) { | ||
| 147 | IPC::RequestParser rp{ctx}; | ||
| 148 | const auto device_handle{rp.Pop<u64>()}; | ||
| 149 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 150 | |||
| 151 | if (state == State::NonInitialized) { | ||
| 152 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 153 | rb.Push(ErrCodes::NfcDisabled); | ||
| 154 | return; | ||
| 155 | } | ||
| 156 | |||
| 157 | // TODO(german77): Loop through all interfaces | ||
| 158 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 159 | const auto result = nfp_interface.StopDetection(); | ||
| 160 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 161 | rb.Push(result); | ||
| 162 | return; | ||
| 163 | } | ||
| 164 | |||
| 165 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 166 | |||
| 167 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 168 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 169 | } | ||
| 170 | |||
| 171 | void IUser::Mount(Kernel::HLERequestContext& ctx) { | ||
| 172 | IPC::RequestParser rp{ctx}; | ||
| 173 | const auto device_handle{rp.Pop<u64>()}; | ||
| 174 | const auto model_type{rp.PopEnum<ModelType>()}; | ||
| 175 | const auto mount_target{rp.PopEnum<MountTarget>()}; | ||
| 176 | LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, | ||
| 177 | model_type, mount_target); | ||
| 178 | |||
| 179 | if (state == State::NonInitialized) { | ||
| 180 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 181 | rb.Push(ErrCodes::NfcDisabled); | ||
| 182 | return; | ||
| 183 | } | ||
| 184 | |||
| 185 | // TODO(german77): Loop through all interfaces | ||
| 186 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 187 | const auto result = nfp_interface.Mount(); | ||
| 188 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 189 | rb.Push(result); | ||
| 190 | return; | ||
| 191 | } | ||
| 192 | |||
| 193 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 194 | |||
| 195 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 196 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 197 | } | ||
| 198 | |||
| 199 | void IUser::Unmount(Kernel::HLERequestContext& ctx) { | ||
| 200 | IPC::RequestParser rp{ctx}; | ||
| 201 | const auto device_handle{rp.Pop<u64>()}; | ||
| 202 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 203 | |||
| 204 | if (state == State::NonInitialized) { | ||
| 205 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 206 | rb.Push(ErrCodes::NfcDisabled); | ||
| 207 | return; | ||
| 208 | } | ||
| 209 | |||
| 210 | // TODO(german77): Loop through all interfaces | ||
| 211 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 212 | const auto result = nfp_interface.Unmount(); | ||
| 213 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 214 | rb.Push(result); | ||
| 215 | return; | ||
| 216 | } | ||
| 217 | |||
| 218 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 219 | |||
| 220 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 221 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 222 | } | ||
| 223 | |||
| 224 | void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 225 | IPC::RequestParser rp{ctx}; | ||
| 226 | const auto device_handle{rp.Pop<u64>()}; | ||
| 227 | const auto access_id{rp.Pop<u32>()}; | ||
| 228 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle, | ||
| 229 | access_id); | ||
| 230 | |||
| 231 | if (state == State::NonInitialized) { | ||
| 232 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 233 | rb.Push(ErrCodes::NfcDisabled); | ||
| 234 | return; | ||
| 235 | } | ||
| 236 | |||
| 237 | // TODO(german77): Loop through all interfaces | ||
| 238 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 239 | const auto result = nfp_interface.OpenApplicationArea(access_id); | ||
| 240 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 241 | rb.Push(result); | ||
| 242 | return; | ||
| 243 | } | ||
| 244 | |||
| 245 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 246 | |||
| 247 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 248 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 249 | } | ||
| 250 | |||
| 251 | void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 252 | IPC::RequestParser rp{ctx}; | ||
| 253 | const auto device_handle{rp.Pop<u64>()}; | ||
| 254 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 255 | |||
| 256 | if (state == State::NonInitialized) { | ||
| 257 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 258 | rb.Push(ErrCodes::NfcDisabled); | ||
| 259 | return; | ||
| 260 | } | ||
| 261 | |||
| 262 | // TODO(german77): Loop through all interfaces | ||
| 263 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 264 | ApplicationArea data{}; | ||
| 265 | const auto result = nfp_interface.GetApplicationArea(data); | ||
| 266 | ctx.WriteBuffer(data); | ||
| 267 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 268 | rb.Push(result); | ||
| 269 | rb.Push(static_cast<u32>(data.size())); | ||
| 270 | return; | ||
| 271 | } | ||
| 272 | |||
| 273 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 274 | |||
| 275 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 276 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 277 | } | ||
| 278 | |||
| 279 | void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 280 | IPC::RequestParser rp{ctx}; | ||
| 281 | const auto device_handle{rp.Pop<u64>()}; | ||
| 282 | const auto data{ctx.ReadBuffer()}; | ||
| 283 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle, | ||
| 284 | data.size()); | ||
| 285 | |||
| 286 | if (state == State::NonInitialized) { | ||
| 287 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 288 | rb.Push(ErrCodes::NfcDisabled); | ||
| 289 | return; | ||
| 290 | } | ||
| 291 | |||
| 292 | // TODO(german77): Loop through all interfaces | ||
| 293 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 294 | const auto result = nfp_interface.SetApplicationArea(data); | ||
| 295 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 296 | rb.Push(result); | ||
| 297 | return; | ||
| 298 | } | ||
| 299 | |||
| 300 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 301 | |||
| 302 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 303 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 304 | } | ||
| 305 | |||
| 306 | void IUser::Flush(Kernel::HLERequestContext& ctx) { | ||
| 307 | IPC::RequestParser rp{ctx}; | ||
| 308 | const auto device_handle{rp.Pop<u64>()}; | ||
| 309 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); | ||
| 310 | |||
| 311 | if (state == State::NonInitialized) { | ||
| 312 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 313 | rb.Push(ErrCodes::NfcDisabled); | ||
| 314 | return; | ||
| 315 | } | ||
| 316 | |||
| 317 | // TODO(german77): Loop through all interfaces | ||
| 318 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 319 | const auto result = nfp_interface.Flush(); | ||
| 320 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 321 | rb.Push(result); | ||
| 322 | return; | ||
| 323 | } | ||
| 324 | |||
| 325 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 326 | |||
| 327 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 328 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 329 | } | ||
| 330 | |||
| 331 | void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 332 | IPC::RequestParser rp{ctx}; | ||
| 333 | const auto device_handle{rp.Pop<u64>()}; | ||
| 334 | const auto access_id{rp.Pop<u32>()}; | ||
| 335 | const auto data{ctx.ReadBuffer()}; | ||
| 336 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}", | ||
| 337 | device_handle, access_id, data.size()); | ||
| 338 | |||
| 339 | if (state == State::NonInitialized) { | ||
| 340 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 341 | rb.Push(ErrCodes::NfcDisabled); | ||
| 342 | return; | ||
| 343 | } | ||
| 344 | |||
| 345 | // TODO(german77): Loop through all interfaces | ||
| 346 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 347 | const auto result = nfp_interface.CreateApplicationArea(access_id, data); | ||
| 348 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 349 | rb.Push(result); | ||
| 350 | return; | ||
| 351 | } | ||
| 352 | |||
| 353 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 354 | |||
| 355 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 356 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 357 | } | ||
| 358 | |||
| 359 | void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { | ||
| 360 | IPC::RequestParser rp{ctx}; | ||
| 361 | const auto device_handle{rp.Pop<u64>()}; | ||
| 362 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 363 | |||
| 364 | if (state == State::NonInitialized) { | ||
| 365 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 366 | rb.Push(ErrCodes::NfcDisabled); | ||
| 367 | return; | ||
| 368 | } | ||
| 369 | |||
| 370 | // TODO(german77): Loop through all interfaces | ||
| 371 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 372 | TagInfo tag_info{}; | ||
| 373 | const auto result = nfp_interface.GetTagInfo(tag_info); | ||
| 374 | ctx.WriteBuffer(tag_info); | ||
| 375 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 376 | rb.Push(result); | ||
| 377 | return; | ||
| 378 | } | ||
| 379 | |||
| 380 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 381 | |||
| 382 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 383 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 384 | } | ||
| 385 | |||
| 386 | void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) { | ||
| 387 | IPC::RequestParser rp{ctx}; | ||
| 388 | const auto device_handle{rp.Pop<u64>()}; | ||
| 389 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 390 | |||
| 391 | if (state == State::NonInitialized) { | ||
| 392 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 393 | rb.Push(ErrCodes::NfcDisabled); | ||
| 394 | return; | ||
| 395 | } | ||
| 396 | |||
| 397 | // TODO(german77): Loop through all interfaces | ||
| 398 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 399 | RegisterInfo register_info{}; | ||
| 400 | const auto result = nfp_interface.GetRegisterInfo(register_info); | ||
| 401 | ctx.WriteBuffer(register_info); | ||
| 402 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 403 | rb.Push(result); | ||
| 404 | return; | ||
| 405 | } | ||
| 406 | |||
| 407 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 408 | |||
| 409 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 410 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 411 | } | ||
| 412 | |||
| 413 | void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) { | ||
| 414 | IPC::RequestParser rp{ctx}; | ||
| 415 | const auto device_handle{rp.Pop<u64>()}; | ||
| 416 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 417 | |||
| 418 | if (state == State::NonInitialized) { | ||
| 419 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 420 | rb.Push(ErrCodes::NfcDisabled); | ||
| 421 | return; | ||
| 422 | } | ||
| 423 | |||
| 424 | // TODO(german77): Loop through all interfaces | ||
| 425 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 426 | CommonInfo common_info{}; | ||
| 427 | const auto result = nfp_interface.GetCommonInfo(common_info); | ||
| 428 | ctx.WriteBuffer(common_info); | ||
| 429 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 430 | rb.Push(result); | ||
| 431 | return; | ||
| 432 | } | ||
| 433 | |||
| 434 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 435 | |||
| 436 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 437 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 438 | } | ||
| 439 | |||
| 440 | void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) { | ||
| 441 | IPC::RequestParser rp{ctx}; | ||
| 442 | const auto device_handle{rp.Pop<u64>()}; | ||
| 443 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 444 | |||
| 445 | if (state == State::NonInitialized) { | ||
| 446 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 447 | rb.Push(ErrCodes::NfcDisabled); | ||
| 448 | return; | ||
| 449 | } | ||
| 450 | |||
| 451 | // TODO(german77): Loop through all interfaces | ||
| 452 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 453 | ModelInfo model_info{}; | ||
| 454 | const auto result = nfp_interface.GetModelInfo(model_info); | ||
| 455 | ctx.WriteBuffer(model_info); | ||
| 456 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 457 | rb.Push(result); | ||
| 458 | return; | ||
| 459 | } | ||
| 460 | |||
| 461 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 462 | |||
| 463 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 464 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 465 | } | ||
| 466 | |||
| 467 | void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { | ||
| 468 | IPC::RequestParser rp{ctx}; | ||
| 469 | const auto device_handle{rp.Pop<u64>()}; | ||
| 470 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 471 | |||
| 472 | if (state == State::NonInitialized) { | ||
| 473 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 474 | rb.Push(ErrCodes::NfcDisabled); | ||
| 475 | return; | ||
| 476 | } | ||
| 477 | |||
| 478 | // TODO(german77): Loop through all interfaces | ||
| 479 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 480 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 481 | rb.Push(ResultSuccess); | ||
| 482 | rb.PushCopyObjects(nfp_interface.GetActivateEvent()); | ||
| 483 | return; | ||
| 484 | } | ||
| 485 | |||
| 486 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 487 | |||
| 488 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 489 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 490 | } | ||
| 491 | |||
| 492 | void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { | ||
| 493 | IPC::RequestParser rp{ctx}; | ||
| 494 | const auto device_handle{rp.Pop<u64>()}; | ||
| 495 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 496 | |||
| 497 | if (state == State::NonInitialized) { | ||
| 498 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 499 | rb.Push(ErrCodes::NfcDisabled); | ||
| 500 | return; | ||
| 501 | } | ||
| 502 | |||
| 503 | // TODO(german77): Loop through all interfaces | ||
| 504 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 505 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 506 | rb.Push(ResultSuccess); | ||
| 507 | rb.PushCopyObjects(nfp_interface.GetDeactivateEvent()); | ||
| 508 | return; | ||
| 509 | } | ||
| 510 | |||
| 511 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 512 | |||
| 513 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 514 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 515 | } | ||
| 516 | |||
| 517 | void IUser::GetState(Kernel::HLERequestContext& ctx) { | ||
| 518 | LOG_DEBUG(Service_NFC, "called"); | ||
| 519 | |||
| 520 | IPC::ResponseBuilder rb{ctx, 3, 0}; | ||
| 521 | rb.Push(ResultSuccess); | ||
| 522 | rb.PushEnum(state); | ||
| 523 | } | ||
| 524 | |||
| 525 | void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { | ||
| 526 | IPC::RequestParser rp{ctx}; | ||
| 527 | const auto device_handle{rp.Pop<u64>()}; | ||
| 528 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 529 | |||
| 530 | // TODO(german77): Loop through all interfaces | ||
| 531 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 532 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 533 | rb.Push(ResultSuccess); | ||
| 534 | rb.PushEnum(nfp_interface.GetCurrentState()); | ||
| 535 | return; | ||
| 536 | } | ||
| 537 | |||
| 538 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 539 | |||
| 540 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 541 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 542 | } | ||
| 543 | |||
| 544 | void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { | ||
| 545 | IPC::RequestParser rp{ctx}; | ||
| 546 | const auto device_handle{rp.Pop<u64>()}; | ||
| 547 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 548 | |||
| 549 | if (state == State::NonInitialized) { | ||
| 550 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 551 | rb.Push(ErrCodes::NfcDisabled); | ||
| 552 | return; | ||
| 553 | } | ||
| 554 | |||
| 555 | // TODO(german77): Loop through all interfaces | ||
| 556 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 557 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 558 | rb.Push(ResultSuccess); | ||
| 559 | rb.PushEnum(nfp_interface.GetNpadId()); | ||
| 560 | return; | ||
| 561 | } | ||
| 562 | |||
| 563 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 564 | |||
| 565 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 566 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 567 | } | ||
| 568 | |||
| 569 | void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { | ||
| 570 | IPC::RequestParser rp{ctx}; | ||
| 571 | const auto device_handle{rp.Pop<u64>()}; | ||
| 572 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 573 | |||
| 574 | // TODO(german77): Loop through all interfaces | ||
| 575 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 576 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 577 | rb.Push(ResultSuccess); | ||
| 578 | rb.Push(sizeof(ApplicationArea)); | ||
| 579 | return; | ||
| 580 | } | ||
| 581 | |||
| 582 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 583 | |||
| 584 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 585 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 586 | } | ||
| 587 | |||
| 588 | void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { | ||
| 589 | LOG_DEBUG(Service_NFP, "(STUBBED) called"); | ||
| 590 | |||
| 591 | if (state == State::NonInitialized) { | ||
| 592 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 593 | rb.Push(ErrCodes::NfcDisabled); | ||
| 594 | return; | ||
| 595 | } | ||
| 596 | |||
| 597 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 598 | rb.Push(ResultSuccess); | ||
| 599 | rb.PushCopyObjects(availability_change_event->GetReadableEvent()); | ||
| 600 | } | ||
| 601 | |||
| 602 | void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 603 | IPC::RequestParser rp{ctx}; | ||
| 604 | const auto device_handle{rp.Pop<u64>()}; | ||
| 605 | const auto access_id{rp.Pop<u32>()}; | ||
| 606 | const auto data{ctx.ReadBuffer()}; | ||
| 607 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}", | ||
| 608 | device_handle, access_id, data.size()); | ||
| 609 | |||
| 610 | if (state == State::NonInitialized) { | ||
| 611 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 612 | rb.Push(ErrCodes::NfcDisabled); | ||
| 613 | return; | ||
| 614 | } | ||
| 615 | |||
| 616 | // TODO(german77): Loop through all interfaces | ||
| 617 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 618 | const auto result = nfp_interface.RecreateApplicationArea(access_id, data); | ||
| 619 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 620 | rb.Push(result); | ||
| 621 | return; | ||
| 622 | } | ||
| 623 | |||
| 624 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); | ||
| 625 | |||
| 626 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 627 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 628 | } | ||
| 629 | |||
| 630 | Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, | ||
| 631 | const char* name) | ||
| 632 | : ServiceFramework{system_, name}, module{std::move(module_)}, | ||
| 633 | npad_id{Core::HID::NpadIdType::Player1}, service_context{system_, service_name} { | ||
| 634 | activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); | ||
| 635 | deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); | ||
| 636 | } | ||
| 637 | |||
| 638 | Module::Interface::~Interface() = default; | ||
| 639 | |||
| 640 | void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { | ||
| 641 | LOG_DEBUG(Service_NFP, "called"); | ||
| 642 | |||
| 643 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 644 | rb.Push(ResultSuccess); | ||
| 645 | rb.PushIpcInterface<IUser>(*this, system); | ||
| 646 | } | ||
| 647 | |||
| 648 | bool Module::Interface::LoadAmiiboFile(const std::string& filename) { | ||
| 649 | constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); | ||
| 650 | const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read, | ||
| 651 | Common::FS::FileType::BinaryFile}; | ||
| 652 | |||
| 653 | if (!amiibo_file.IsOpen()) { | ||
| 654 | LOG_ERROR(Service_NFP, "Amiibo is already on use"); | ||
| 655 | return false; | ||
| 656 | } | ||
| 657 | |||
| 658 | // Workaround for files with missing password data | ||
| 659 | std::array<u8, sizeof(EncryptedNTAG215File)> buffer{}; | ||
| 660 | if (amiibo_file.Read(buffer) < tag_size_without_password) { | ||
| 661 | LOG_ERROR(Service_NFP, "Failed to read amiibo file"); | ||
| 662 | return false; | ||
| 663 | } | ||
| 664 | memcpy(&encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File)); | ||
| 665 | |||
| 666 | if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { | ||
| 667 | LOG_INFO(Service_NFP, "Invalid amiibo"); | ||
| 668 | return false; | ||
| 669 | } | ||
| 670 | |||
| 671 | file_path = filename; | ||
| 672 | return true; | ||
| 673 | } | ||
| 674 | |||
| 675 | bool Module::Interface::LoadAmiibo(const std::string& filename) { | ||
| 676 | if (device_state != DeviceState::SearchingForTag) { | ||
| 677 | LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); | ||
| 678 | return false; | ||
| 679 | } | ||
| 680 | |||
| 681 | if (!LoadAmiiboFile(filename)) { | ||
| 682 | return false; | ||
| 683 | } | ||
| 684 | |||
| 685 | device_state = DeviceState::TagFound; | ||
| 686 | activate_event->GetWritableEvent().Signal(); | ||
| 687 | return true; | ||
| 688 | } | ||
| 689 | |||
| 690 | void Module::Interface::CloseAmiibo() { | ||
| 691 | LOG_INFO(Service_NFP, "Remove amiibo"); | ||
| 692 | device_state = DeviceState::TagRemoved; | ||
| 693 | is_data_decoded = false; | ||
| 694 | is_application_area_initialized = false; | ||
| 695 | encrypted_tag_data = {}; | ||
| 696 | tag_data = {}; | ||
| 697 | deactivate_event->GetWritableEvent().Signal(); | ||
| 698 | } | ||
| 699 | |||
| 700 | Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const { | ||
| 701 | return activate_event->GetReadableEvent(); | ||
| 702 | } | ||
| 703 | |||
| 704 | Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const { | ||
| 705 | return deactivate_event->GetReadableEvent(); | ||
| 706 | } | ||
| 707 | |||
| 708 | void Module::Interface::Initialize() { | ||
| 709 | device_state = DeviceState::Initialized; | ||
| 710 | is_data_decoded = false; | ||
| 711 | is_application_area_initialized = false; | ||
| 712 | encrypted_tag_data = {}; | ||
| 713 | tag_data = {}; | ||
| 714 | } | ||
| 715 | |||
| 716 | void Module::Interface::Finalize() { | ||
| 717 | if (device_state == DeviceState::TagMounted) { | ||
| 718 | Unmount(); | ||
| 719 | } | ||
| 720 | if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { | ||
| 721 | StopDetection(); | ||
| 722 | } | ||
| 723 | device_state = DeviceState::Unaviable; | ||
| 724 | } | ||
| 725 | |||
| 726 | Result Module::Interface::StartDetection(s32 protocol_) { | ||
| 727 | auto npad_device = system.HIDCore().GetEmulatedController(npad_id); | ||
| 728 | |||
| 729 | // TODO(german77): Add callback for when nfc data is available | ||
| 730 | |||
| 731 | if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { | ||
| 732 | npad_device->SetPollingMode(Common::Input::PollingMode::NFC); | ||
| 733 | device_state = DeviceState::SearchingForTag; | ||
| 734 | protocol = protocol_; | ||
| 735 | return ResultSuccess; | ||
| 736 | } | ||
| 737 | |||
| 738 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 739 | return ErrCodes::WrongDeviceState; | ||
| 740 | } | ||
| 741 | |||
| 742 | Result Module::Interface::StopDetection() { | ||
| 743 | auto npad_device = system.HIDCore().GetEmulatedController(npad_id); | ||
| 744 | npad_device->SetPollingMode(Common::Input::PollingMode::Active); | ||
| 745 | |||
| 746 | if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { | ||
| 747 | CloseAmiibo(); | ||
| 748 | return ResultSuccess; | ||
| 749 | } | ||
| 750 | if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { | ||
| 751 | device_state = DeviceState::Initialized; | ||
| 752 | return ResultSuccess; | ||
| 753 | } | ||
| 754 | |||
| 755 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 756 | return ErrCodes::WrongDeviceState; | ||
| 757 | } | ||
| 758 | |||
| 759 | Result Module::Interface::Flush() { | ||
| 760 | // Ignore write command if we can't encrypt the data | ||
| 761 | if (!is_data_decoded) { | ||
| 762 | return ResultSuccess; | ||
| 763 | } | ||
| 764 | |||
| 765 | constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); | ||
| 766 | EncryptedNTAG215File tmp_encrypted_tag_data{}; | ||
| 767 | const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite, | ||
| 768 | Common::FS::FileType::BinaryFile}; | ||
| 769 | |||
| 770 | if (!amiibo_file.IsOpen()) { | ||
| 771 | LOG_ERROR(Core, "Amiibo is already on use"); | ||
| 772 | return ErrCodes::WriteAmiiboFailed; | ||
| 773 | } | ||
| 774 | |||
| 775 | // Workaround for files with missing password data | ||
| 776 | std::array<u8, sizeof(EncryptedNTAG215File)> buffer{}; | ||
| 777 | if (amiibo_file.Read(buffer) < tag_size_without_password) { | ||
| 778 | LOG_ERROR(Core, "Failed to read amiibo file"); | ||
| 779 | return ErrCodes::WriteAmiiboFailed; | ||
| 780 | } | ||
| 781 | memcpy(&tmp_encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File)); | ||
| 782 | |||
| 783 | if (!AmiiboCrypto::IsAmiiboValid(tmp_encrypted_tag_data)) { | ||
| 784 | LOG_INFO(Service_NFP, "Invalid amiibo"); | ||
| 785 | return ErrCodes::WriteAmiiboFailed; | ||
| 786 | } | ||
| 787 | |||
| 788 | bool is_uuid_equal = memcmp(tmp_encrypted_tag_data.uuid.data(), tag_data.uuid.data(), 8) == 0; | ||
| 789 | bool is_character_equal = tmp_encrypted_tag_data.user_memory.model_info.character_id == | ||
| 790 | tag_data.model_info.character_id; | ||
| 791 | if (!is_uuid_equal || !is_character_equal) { | ||
| 792 | LOG_ERROR(Service_NFP, "Not the same amiibo"); | ||
| 793 | return ErrCodes::WriteAmiiboFailed; | ||
| 794 | } | ||
| 795 | |||
| 796 | if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { | ||
| 797 | LOG_ERROR(Service_NFP, "Failed to encode data"); | ||
| 798 | return ErrCodes::WriteAmiiboFailed; | ||
| 799 | } | ||
| 800 | |||
| 801 | // Return to the start of the file | ||
| 802 | if (!amiibo_file.Seek(0)) { | ||
| 803 | LOG_ERROR(Service_NFP, "Error writting to file"); | ||
| 804 | return ErrCodes::WriteAmiiboFailed; | ||
| 805 | } | ||
| 806 | |||
| 807 | if (!amiibo_file.Write(encrypted_tag_data)) { | ||
| 808 | LOG_ERROR(Service_NFP, "Error writting to file"); | ||
| 809 | return ErrCodes::WriteAmiiboFailed; | ||
| 810 | } | ||
| 811 | |||
| 812 | return ResultSuccess; | ||
| 813 | } | ||
| 814 | |||
| 815 | Result Module::Interface::Mount() { | ||
| 816 | if (device_state != DeviceState::TagFound) { | ||
| 817 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 818 | return ErrCodes::WrongDeviceState; | ||
| 819 | } | ||
| 820 | 10 | ||
| 821 | is_data_decoded = AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data); | 11 | class IUserManager final : public ServiceFramework<IUserManager> { |
| 822 | LOG_INFO(Service_NFP, "Is amiibo decoded {}", is_data_decoded); | 12 | public: |
| 823 | 13 | explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} { | |
| 824 | is_application_area_initialized = false; | 14 | // clang-format off |
| 825 | device_state = DeviceState::TagMounted; | 15 | static const FunctionInfo functions[] = { |
| 826 | return ResultSuccess; | 16 | {0, &IUserManager::CreateUserInterface, "CreateUserInterface"}, |
| 827 | } | ||
| 828 | |||
| 829 | Result Module::Interface::Unmount() { | ||
| 830 | if (device_state != DeviceState::TagMounted) { | ||
| 831 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 832 | return ErrCodes::WrongDeviceState; | ||
| 833 | } | ||
| 834 | |||
| 835 | is_data_decoded = false; | ||
| 836 | is_application_area_initialized = false; | ||
| 837 | device_state = DeviceState::TagFound; | ||
| 838 | return ResultSuccess; | ||
| 839 | } | ||
| 840 | |||
| 841 | Result Module::Interface::GetTagInfo(TagInfo& tag_info) const { | ||
| 842 | if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { | ||
| 843 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 844 | return ErrCodes::WrongDeviceState; | ||
| 845 | } | ||
| 846 | |||
| 847 | tag_info = { | ||
| 848 | .uuid = encrypted_tag_data.uuid, | ||
| 849 | .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.size()), | ||
| 850 | .protocol = protocol, | ||
| 851 | .tag_type = static_cast<u32>(encrypted_tag_data.user_memory.model_info.amiibo_type), | ||
| 852 | }; | ||
| 853 | |||
| 854 | return ResultSuccess; | ||
| 855 | } | ||
| 856 | |||
| 857 | Result Module::Interface::GetCommonInfo(CommonInfo& common_info) const { | ||
| 858 | if (device_state != DeviceState::TagMounted) { | ||
| 859 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 860 | return ErrCodes::WrongDeviceState; | ||
| 861 | } | ||
| 862 | |||
| 863 | if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) { | ||
| 864 | const auto& settings = tag_data.settings; | ||
| 865 | // TODO: Validate this data | ||
| 866 | common_info = { | ||
| 867 | .last_write_year = settings.write_date.GetYear(), | ||
| 868 | .last_write_month = settings.write_date.GetMonth(), | ||
| 869 | .last_write_day = settings.write_date.GetDay(), | ||
| 870 | .write_counter = settings.crc_counter, | ||
| 871 | .version = 1, | ||
| 872 | .application_area_size = sizeof(ApplicationArea), | ||
| 873 | }; | ||
| 874 | return ResultSuccess; | ||
| 875 | } | ||
| 876 | |||
| 877 | // Generate a generic answer | ||
| 878 | common_info = { | ||
| 879 | .last_write_year = 2022, | ||
| 880 | .last_write_month = 2, | ||
| 881 | .last_write_day = 7, | ||
| 882 | .write_counter = 0, | ||
| 883 | .version = 1, | ||
| 884 | .application_area_size = sizeof(ApplicationArea), | ||
| 885 | }; | ||
| 886 | return ResultSuccess; | ||
| 887 | } | ||
| 888 | |||
| 889 | Result Module::Interface::GetModelInfo(ModelInfo& model_info) const { | ||
| 890 | if (device_state != DeviceState::TagMounted) { | ||
| 891 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 892 | return ErrCodes::WrongDeviceState; | ||
| 893 | } | ||
| 894 | |||
| 895 | const auto& model_info_data = encrypted_tag_data.user_memory.model_info; | ||
| 896 | model_info = { | ||
| 897 | .character_id = model_info_data.character_id, | ||
| 898 | .character_variant = model_info_data.character_variant, | ||
| 899 | .amiibo_type = model_info_data.amiibo_type, | ||
| 900 | .model_number = model_info_data.model_number, | ||
| 901 | .series = model_info_data.series, | ||
| 902 | .constant_value = model_info_data.constant_value, | ||
| 903 | }; | ||
| 904 | return ResultSuccess; | ||
| 905 | } | ||
| 906 | |||
| 907 | Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const { | ||
| 908 | if (device_state != DeviceState::TagMounted) { | ||
| 909 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 910 | if (device_state == DeviceState::TagRemoved) { | ||
| 911 | return ErrCodes::TagRemoved; | ||
| 912 | } | ||
| 913 | return ErrCodes::WrongDeviceState; | ||
| 914 | } | ||
| 915 | |||
| 916 | Service::Mii::MiiManager manager; | ||
| 917 | |||
| 918 | if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) { | ||
| 919 | const auto& settings = tag_data.settings; | ||
| 920 | |||
| 921 | // TODO: Validate this data | ||
| 922 | register_info = { | ||
| 923 | .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), | ||
| 924 | .first_write_year = settings.init_date.GetYear(), | ||
| 925 | .first_write_month = settings.init_date.GetMonth(), | ||
| 926 | .first_write_day = settings.init_date.GetDay(), | ||
| 927 | .amiibo_name = GetAmiiboName(settings), | ||
| 928 | .font_region = {}, | ||
| 929 | }; | 17 | }; |
| 18 | // clang-format on | ||
| 930 | 19 | ||
| 931 | return ResultSuccess; | 20 | RegisterHandlers(functions); |
| 932 | } | 21 | } |
| 933 | 22 | ||
| 934 | // Generate a generic answer | 23 | private: |
| 935 | register_info = { | 24 | void CreateUserInterface(Kernel::HLERequestContext& ctx) { |
| 936 | .mii_char_info = manager.BuildDefault(0), | 25 | LOG_DEBUG(Service_NFP, "called"); |
| 937 | .first_write_year = 2022, | ||
| 938 | .first_write_month = 2, | ||
| 939 | .first_write_day = 7, | ||
| 940 | .amiibo_name = {'Y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o', 0}, | ||
| 941 | .font_region = {}, | ||
| 942 | }; | ||
| 943 | return ResultSuccess; | ||
| 944 | } | ||
| 945 | 26 | ||
| 946 | Result Module::Interface::OpenApplicationArea(u32 access_id) { | 27 | if (user_interface == nullptr) { |
| 947 | if (device_state != DeviceState::TagMounted) { | 28 | user_interface = std::make_shared<IUser>(system); |
| 948 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 949 | if (device_state == DeviceState::TagRemoved) { | ||
| 950 | return ErrCodes::TagRemoved; | ||
| 951 | } | 29 | } |
| 952 | return ErrCodes::WrongDeviceState; | ||
| 953 | } | ||
| 954 | |||
| 955 | // Fallback for lack of amiibo keys | ||
| 956 | if (!is_data_decoded) { | ||
| 957 | LOG_WARNING(Service_NFP, "Application area is not initialized"); | ||
| 958 | return ErrCodes::ApplicationAreaIsNotInitialized; | ||
| 959 | } | ||
| 960 | 30 | ||
| 961 | if (tag_data.settings.settings.appdata_initialized == 0) { | 31 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 962 | LOG_WARNING(Service_NFP, "Application area is not initialized"); | 32 | rb.Push(ResultSuccess); |
| 963 | return ErrCodes::ApplicationAreaIsNotInitialized; | 33 | rb.PushIpcInterface<IUser>(user_interface); |
| 964 | } | ||
| 965 | |||
| 966 | if (tag_data.application_area_id != access_id) { | ||
| 967 | LOG_WARNING(Service_NFP, "Wrong application area id"); | ||
| 968 | return ErrCodes::WrongApplicationAreaId; | ||
| 969 | } | ||
| 970 | |||
| 971 | is_application_area_initialized = true; | ||
| 972 | return ResultSuccess; | ||
| 973 | } | ||
| 974 | |||
| 975 | Result Module::Interface::GetApplicationArea(ApplicationArea& data) const { | ||
| 976 | if (device_state != DeviceState::TagMounted) { | ||
| 977 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 978 | if (device_state == DeviceState::TagRemoved) { | ||
| 979 | return ErrCodes::TagRemoved; | ||
| 980 | } | ||
| 981 | return ErrCodes::WrongDeviceState; | ||
| 982 | } | ||
| 983 | |||
| 984 | if (!is_application_area_initialized) { | ||
| 985 | LOG_ERROR(Service_NFP, "Application area is not initialized"); | ||
| 986 | return ErrCodes::ApplicationAreaIsNotInitialized; | ||
| 987 | } | ||
| 988 | |||
| 989 | data = tag_data.application_area; | ||
| 990 | |||
| 991 | return ResultSuccess; | ||
| 992 | } | ||
| 993 | |||
| 994 | Result Module::Interface::SetApplicationArea(const std::vector<u8>& data) { | ||
| 995 | if (device_state != DeviceState::TagMounted) { | ||
| 996 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 997 | if (device_state == DeviceState::TagRemoved) { | ||
| 998 | return ErrCodes::TagRemoved; | ||
| 999 | } | ||
| 1000 | return ErrCodes::WrongDeviceState; | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | if (!is_application_area_initialized) { | ||
| 1004 | LOG_ERROR(Service_NFP, "Application area is not initialized"); | ||
| 1005 | return ErrCodes::ApplicationAreaIsNotInitialized; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | if (data.size() != sizeof(ApplicationArea)) { | ||
| 1009 | LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); | ||
| 1010 | return ResultUnknown; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); | ||
| 1014 | return ResultSuccess; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | Result Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) { | ||
| 1018 | if (device_state != DeviceState::TagMounted) { | ||
| 1019 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 1020 | if (device_state == DeviceState::TagRemoved) { | ||
| 1021 | return ErrCodes::TagRemoved; | ||
| 1022 | } | ||
| 1023 | return ErrCodes::WrongDeviceState; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | if (tag_data.settings.settings.appdata_initialized != 0) { | ||
| 1027 | LOG_ERROR(Service_NFP, "Application area already exist"); | ||
| 1028 | return ErrCodes::ApplicationAreaExist; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | if (data.size() != sizeof(ApplicationArea)) { | ||
| 1032 | LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); | ||
| 1033 | return ResultUnknown; | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); | ||
| 1037 | tag_data.application_area_id = access_id; | ||
| 1038 | |||
| 1039 | return ResultSuccess; | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | Result Module::Interface::RecreateApplicationArea(u32 access_id, const std::vector<u8>& data) { | ||
| 1043 | if (device_state != DeviceState::TagMounted) { | ||
| 1044 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 1045 | if (device_state == DeviceState::TagRemoved) { | ||
| 1046 | return ErrCodes::TagRemoved; | ||
| 1047 | } | ||
| 1048 | return ErrCodes::WrongDeviceState; | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | if (data.size() != sizeof(ApplicationArea)) { | ||
| 1052 | LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); | ||
| 1053 | return ResultUnknown; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); | ||
| 1057 | tag_data.application_area_id = access_id; | ||
| 1058 | |||
| 1059 | return ResultSuccess; | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | u64 Module::Interface::GetHandle() const { | ||
| 1063 | // Generate a handle based of the npad id | ||
| 1064 | return static_cast<u64>(npad_id); | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | DeviceState Module::Interface::GetCurrentState() const { | ||
| 1068 | return device_state; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | Core::HID::NpadIdType Module::Interface::GetNpadId() const { | ||
| 1072 | // Return first connected npad id as a workaround for lack of a single nfc interface per | ||
| 1073 | // controller | ||
| 1074 | return system.HIDCore().GetFirstNpadId(); | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | AmiiboName Module::Interface::GetAmiiboName(const AmiiboSettings& settings) const { | ||
| 1078 | std::array<char16_t, amiibo_name_length> settings_amiibo_name{}; | ||
| 1079 | AmiiboName amiibo_name{}; | ||
| 1080 | |||
| 1081 | // Convert from big endian to little endian | ||
| 1082 | for (std::size_t i = 0; i < amiibo_name_length; i++) { | ||
| 1083 | settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]); | ||
| 1084 | } | 34 | } |
| 1085 | 35 | ||
| 1086 | // Convert from utf16 to utf8 | 36 | std::shared_ptr<IUser> user_interface; |
| 1087 | const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); | 37 | }; |
| 1088 | memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); | ||
| 1089 | |||
| 1090 | return amiibo_name; | ||
| 1091 | } | ||
| 1092 | 38 | ||
| 1093 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { | 39 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
| 1094 | auto module = std::make_shared<Module>(); | 40 | std::make_shared<IUserManager>(system)->InstallAsService(service_manager); |
| 1095 | std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager); | ||
| 1096 | } | 41 | } |
| 1097 | 42 | ||
| 1098 | } // namespace Service::NFP | 43 | } // namespace Service::NFP |
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 0de0b48e7..a25c362b8 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h | |||
| @@ -3,170 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "common/common_funcs.h" | ||
| 10 | #include "core/hle/service/kernel_helpers.h" | ||
| 11 | #include "core/hle/service/mii/types.h" | ||
| 12 | #include "core/hle/service/nfp/amiibo_types.h" | ||
| 13 | #include "core/hle/service/service.h" | 6 | #include "core/hle/service/service.h" |
| 14 | 7 | ||
| 15 | namespace Kernel { | ||
| 16 | class KEvent; | ||
| 17 | class KReadableEvent; | ||
| 18 | } // namespace Kernel | ||
| 19 | |||
| 20 | namespace Core::HID { | ||
| 21 | enum class NpadIdType : u32; | ||
| 22 | } // namespace Core::HID | ||
| 23 | |||
| 24 | namespace Service::NFP { | 8 | namespace Service::NFP { |
| 25 | using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; | ||
| 26 | |||
| 27 | struct TagInfo { | ||
| 28 | TagUuid uuid; | ||
| 29 | u8 uuid_length; | ||
| 30 | INSERT_PADDING_BYTES(0x15); | ||
| 31 | s32 protocol; | ||
| 32 | u32 tag_type; | ||
| 33 | INSERT_PADDING_BYTES(0x30); | ||
| 34 | }; | ||
| 35 | static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); | ||
| 36 | |||
| 37 | struct CommonInfo { | ||
| 38 | u16 last_write_year; | ||
| 39 | u8 last_write_month; | ||
| 40 | u8 last_write_day; | ||
| 41 | u16 write_counter; | ||
| 42 | u16 version; | ||
| 43 | u32 application_area_size; | ||
| 44 | INSERT_PADDING_BYTES(0x34); | ||
| 45 | }; | ||
| 46 | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||
| 47 | |||
| 48 | struct ModelInfo { | ||
| 49 | u16 character_id; | ||
| 50 | u8 character_variant; | ||
| 51 | AmiiboType amiibo_type; | ||
| 52 | u16 model_number; | ||
| 53 | AmiiboSeries series; | ||
| 54 | u8 constant_value; // Must be 02 | ||
| 55 | INSERT_PADDING_BYTES(0x38); // Unknown | ||
| 56 | }; | ||
| 57 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | ||
| 58 | |||
| 59 | struct RegisterInfo { | ||
| 60 | Service::Mii::CharInfo mii_char_info; | ||
| 61 | u16 first_write_year; | ||
| 62 | u8 first_write_month; | ||
| 63 | u8 first_write_day; | ||
| 64 | AmiiboName amiibo_name; | ||
| 65 | u8 font_region; | ||
| 66 | INSERT_PADDING_BYTES(0x7A); | ||
| 67 | }; | ||
| 68 | static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); | ||
| 69 | |||
| 70 | class Module final { | ||
| 71 | public: | ||
| 72 | class Interface : public ServiceFramework<Interface> { | ||
| 73 | public: | ||
| 74 | explicit Interface(std::shared_ptr<Module> module_, Core::System& system_, | ||
| 75 | const char* name); | ||
| 76 | ~Interface() override; | ||
| 77 | |||
| 78 | void CreateUserInterface(Kernel::HLERequestContext& ctx); | ||
| 79 | bool LoadAmiibo(const std::string& filename); | ||
| 80 | bool LoadAmiiboFile(const std::string& filename); | ||
| 81 | void CloseAmiibo(); | ||
| 82 | |||
| 83 | void Initialize(); | ||
| 84 | void Finalize(); | ||
| 85 | |||
| 86 | Result StartDetection(s32 protocol_); | ||
| 87 | Result StopDetection(); | ||
| 88 | Result Mount(); | ||
| 89 | Result Unmount(); | ||
| 90 | Result Flush(); | ||
| 91 | |||
| 92 | Result GetTagInfo(TagInfo& tag_info) const; | ||
| 93 | Result GetCommonInfo(CommonInfo& common_info) const; | ||
| 94 | Result GetModelInfo(ModelInfo& model_info) const; | ||
| 95 | Result GetRegisterInfo(RegisterInfo& register_info) const; | ||
| 96 | |||
| 97 | Result OpenApplicationArea(u32 access_id); | ||
| 98 | Result GetApplicationArea(ApplicationArea& data) const; | ||
| 99 | Result SetApplicationArea(const std::vector<u8>& data); | ||
| 100 | Result CreateApplicationArea(u32 access_id, const std::vector<u8>& data); | ||
| 101 | Result RecreateApplicationArea(u32 access_id, const std::vector<u8>& data); | ||
| 102 | |||
| 103 | u64 GetHandle() const; | ||
| 104 | DeviceState GetCurrentState() const; | ||
| 105 | Core::HID::NpadIdType GetNpadId() const; | ||
| 106 | |||
| 107 | Kernel::KReadableEvent& GetActivateEvent() const; | ||
| 108 | Kernel::KReadableEvent& GetDeactivateEvent() const; | ||
| 109 | |||
| 110 | protected: | ||
| 111 | std::shared_ptr<Module> module; | ||
| 112 | |||
| 113 | private: | ||
| 114 | AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; | ||
| 115 | |||
| 116 | const Core::HID::NpadIdType npad_id; | ||
| 117 | |||
| 118 | bool is_data_decoded{}; | ||
| 119 | bool is_application_area_initialized{}; | ||
| 120 | s32 protocol; | ||
| 121 | std::string file_path{}; | ||
| 122 | Kernel::KEvent* activate_event; | ||
| 123 | Kernel::KEvent* deactivate_event; | ||
| 124 | DeviceState device_state{DeviceState::Unaviable}; | ||
| 125 | KernelHelpers::ServiceContext service_context; | ||
| 126 | |||
| 127 | NTAG215File tag_data{}; | ||
| 128 | EncryptedNTAG215File encrypted_tag_data{}; | ||
| 129 | }; | ||
| 130 | }; | ||
| 131 | |||
| 132 | class IUser final : public ServiceFramework<IUser> { | ||
| 133 | public: | ||
| 134 | explicit IUser(Module::Interface& nfp_interface_, Core::System& system_); | ||
| 135 | |||
| 136 | private: | ||
| 137 | void Initialize(Kernel::HLERequestContext& ctx); | ||
| 138 | void Finalize(Kernel::HLERequestContext& ctx); | ||
| 139 | void ListDevices(Kernel::HLERequestContext& ctx); | ||
| 140 | void StartDetection(Kernel::HLERequestContext& ctx); | ||
| 141 | void StopDetection(Kernel::HLERequestContext& ctx); | ||
| 142 | void Mount(Kernel::HLERequestContext& ctx); | ||
| 143 | void Unmount(Kernel::HLERequestContext& ctx); | ||
| 144 | void OpenApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 145 | void GetApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 146 | void SetApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 147 | void Flush(Kernel::HLERequestContext& ctx); | ||
| 148 | void CreateApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 149 | void GetTagInfo(Kernel::HLERequestContext& ctx); | ||
| 150 | void GetRegisterInfo(Kernel::HLERequestContext& ctx); | ||
| 151 | void GetCommonInfo(Kernel::HLERequestContext& ctx); | ||
| 152 | void GetModelInfo(Kernel::HLERequestContext& ctx); | ||
| 153 | void AttachActivateEvent(Kernel::HLERequestContext& ctx); | ||
| 154 | void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); | ||
| 155 | void GetState(Kernel::HLERequestContext& ctx); | ||
| 156 | void GetDeviceState(Kernel::HLERequestContext& ctx); | ||
| 157 | void GetNpadId(Kernel::HLERequestContext& ctx); | ||
| 158 | void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); | ||
| 159 | void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); | ||
| 160 | void RecreateApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 161 | |||
| 162 | KernelHelpers::ServiceContext service_context; | ||
| 163 | |||
| 164 | // TODO(german77): We should have a vector of interfaces | ||
| 165 | Module::Interface& nfp_interface; | ||
| 166 | |||
| 167 | State state{State::NonInitialized}; | ||
| 168 | Kernel::KEvent* availability_change_event; | ||
| 169 | }; | ||
| 170 | 9 | ||
| 171 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); | 10 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 172 | 11 | ||
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp new file mode 100644 index 000000000..ec895ac01 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.cpp | |||
| @@ -0,0 +1,681 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <array> | ||
| 5 | #include <atomic> | ||
| 6 | |||
| 7 | #include "common/fs/file.h" | ||
| 8 | #include "common/fs/path_util.h" | ||
| 9 | #include "common/input.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "common/string_util.h" | ||
| 12 | #include "common/tiny_mt.h" | ||
| 13 | #include "core/core.h" | ||
| 14 | #include "core/hid/emulated_controller.h" | ||
| 15 | #include "core/hid/hid_core.h" | ||
| 16 | #include "core/hid/hid_types.h" | ||
| 17 | #include "core/hle/ipc_helpers.h" | ||
| 18 | #include "core/hle/kernel/k_event.h" | ||
| 19 | #include "core/hle/service/mii/mii_manager.h" | ||
| 20 | #include "core/hle/service/nfp/amiibo_crypto.h" | ||
| 21 | #include "core/hle/service/nfp/nfp.h" | ||
| 22 | #include "core/hle/service/nfp/nfp_device.h" | ||
| 23 | #include "core/hle/service/nfp/nfp_result.h" | ||
| 24 | #include "core/hle/service/nfp/nfp_user.h" | ||
| 25 | #include "core/hle/service/time/time_manager.h" | ||
| 26 | #include "core/hle/service/time/time_zone_content_manager.h" | ||
| 27 | #include "core/hle/service/time/time_zone_types.h" | ||
| 28 | |||
| 29 | namespace Service::NFP { | ||
| 30 | NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, | ||
| 31 | KernelHelpers::ServiceContext& service_context_, | ||
| 32 | Kernel::KEvent* availability_change_event_) | ||
| 33 | : npad_id{npad_id_}, system{system_}, service_context{service_context_}, | ||
| 34 | availability_change_event{availability_change_event_} { | ||
| 35 | activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); | ||
| 36 | deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); | ||
| 37 | npad_device = system.HIDCore().GetEmulatedController(npad_id); | ||
| 38 | |||
| 39 | Core::HID::ControllerUpdateCallback engine_callback{ | ||
| 40 | .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); }, | ||
| 41 | .is_npad_service = false, | ||
| 42 | }; | ||
| 43 | is_controller_set = true; | ||
| 44 | callback_key = npad_device->SetCallback(engine_callback); | ||
| 45 | |||
| 46 | auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; | ||
| 47 | current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point; | ||
| 48 | } | ||
| 49 | |||
| 50 | NfpDevice::~NfpDevice() { | ||
| 51 | if (!is_controller_set) { | ||
| 52 | return; | ||
| 53 | } | ||
| 54 | npad_device->DeleteCallback(callback_key); | ||
| 55 | is_controller_set = false; | ||
| 56 | }; | ||
| 57 | |||
| 58 | void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { | ||
| 59 | if (type == Core::HID::ControllerTriggerType::Connected || | ||
| 60 | type == Core::HID::ControllerTriggerType::Disconnected) { | ||
| 61 | availability_change_event->GetWritableEvent().Signal(); | ||
| 62 | return; | ||
| 63 | } | ||
| 64 | |||
| 65 | if (type != Core::HID::ControllerTriggerType::Nfc) { | ||
| 66 | return; | ||
| 67 | } | ||
| 68 | |||
| 69 | if (!npad_device->IsConnected()) { | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | const auto nfc_status = npad_device->GetNfc(); | ||
| 74 | switch (nfc_status.state) { | ||
| 75 | case Common::Input::NfcState::NewAmiibo: | ||
| 76 | LoadAmiibo(nfc_status.data); | ||
| 77 | break; | ||
| 78 | case Common::Input::NfcState::AmiiboRemoved: | ||
| 79 | if (device_state != DeviceState::SearchingForTag) { | ||
| 80 | CloseAmiibo(); | ||
| 81 | } | ||
| 82 | break; | ||
| 83 | default: | ||
| 84 | break; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | bool NfpDevice::LoadAmiibo(std::span<const u8> data) { | ||
| 89 | if (device_state != DeviceState::SearchingForTag) { | ||
| 90 | LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); | ||
| 91 | return false; | ||
| 92 | } | ||
| 93 | |||
| 94 | if (data.size() != sizeof(EncryptedNTAG215File)) { | ||
| 95 | LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size()); | ||
| 96 | return false; | ||
| 97 | } | ||
| 98 | |||
| 99 | memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); | ||
| 100 | |||
| 101 | device_state = DeviceState::TagFound; | ||
| 102 | deactivate_event->GetReadableEvent().Clear(); | ||
| 103 | activate_event->GetWritableEvent().Signal(); | ||
| 104 | return true; | ||
| 105 | } | ||
| 106 | |||
| 107 | void NfpDevice::CloseAmiibo() { | ||
| 108 | LOG_INFO(Service_NFP, "Remove amiibo"); | ||
| 109 | |||
| 110 | if (device_state == DeviceState::TagMounted) { | ||
| 111 | Unmount(); | ||
| 112 | } | ||
| 113 | |||
| 114 | device_state = DeviceState::TagRemoved; | ||
| 115 | encrypted_tag_data = {}; | ||
| 116 | tag_data = {}; | ||
| 117 | activate_event->GetReadableEvent().Clear(); | ||
| 118 | deactivate_event->GetWritableEvent().Signal(); | ||
| 119 | } | ||
| 120 | |||
| 121 | Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const { | ||
| 122 | return activate_event->GetReadableEvent(); | ||
| 123 | } | ||
| 124 | |||
| 125 | Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const { | ||
| 126 | return deactivate_event->GetReadableEvent(); | ||
| 127 | } | ||
| 128 | |||
| 129 | void NfpDevice::Initialize() { | ||
| 130 | device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; | ||
| 131 | encrypted_tag_data = {}; | ||
| 132 | tag_data = {}; | ||
| 133 | } | ||
| 134 | |||
| 135 | void NfpDevice::Finalize() { | ||
| 136 | if (device_state == DeviceState::TagMounted) { | ||
| 137 | Unmount(); | ||
| 138 | } | ||
| 139 | if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { | ||
| 140 | StopDetection(); | ||
| 141 | } | ||
| 142 | device_state = DeviceState::Unavailable; | ||
| 143 | } | ||
| 144 | |||
| 145 | Result NfpDevice::StartDetection(s32 protocol_) { | ||
| 146 | if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { | ||
| 147 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 148 | return WrongDeviceState; | ||
| 149 | } | ||
| 150 | |||
| 151 | if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { | ||
| 152 | LOG_ERROR(Service_NFP, "Nfc not supported"); | ||
| 153 | return NfcDisabled; | ||
| 154 | } | ||
| 155 | |||
| 156 | device_state = DeviceState::SearchingForTag; | ||
| 157 | protocol = protocol_; | ||
| 158 | return ResultSuccess; | ||
| 159 | } | ||
| 160 | |||
| 161 | Result NfpDevice::StopDetection() { | ||
| 162 | npad_device->SetPollingMode(Common::Input::PollingMode::Active); | ||
| 163 | |||
| 164 | if (device_state == DeviceState::Initialized) { | ||
| 165 | return ResultSuccess; | ||
| 166 | } | ||
| 167 | |||
| 168 | if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { | ||
| 169 | CloseAmiibo(); | ||
| 170 | return ResultSuccess; | ||
| 171 | } | ||
| 172 | if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { | ||
| 173 | device_state = DeviceState::Initialized; | ||
| 174 | return ResultSuccess; | ||
| 175 | } | ||
| 176 | |||
| 177 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 178 | return WrongDeviceState; | ||
| 179 | } | ||
| 180 | |||
| 181 | Result NfpDevice::Flush() { | ||
| 182 | if (device_state != DeviceState::TagMounted) { | ||
| 183 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 184 | if (device_state == DeviceState::TagRemoved) { | ||
| 185 | return TagRemoved; | ||
| 186 | } | ||
| 187 | return WrongDeviceState; | ||
| 188 | } | ||
| 189 | |||
| 190 | if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { | ||
| 191 | LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||
| 192 | return WrongDeviceState; | ||
| 193 | } | ||
| 194 | |||
| 195 | auto& settings = tag_data.settings; | ||
| 196 | |||
| 197 | const auto& current_date = GetAmiiboDate(current_posix_time); | ||
| 198 | if (settings.write_date.raw_date != current_date.raw_date) { | ||
| 199 | settings.write_date = current_date; | ||
| 200 | settings.crc_counter++; | ||
| 201 | // TODO: Find how to calculate the crc check | ||
| 202 | // settings.crc = CalculateCRC(settings); | ||
| 203 | } | ||
| 204 | |||
| 205 | tag_data.write_counter++; | ||
| 206 | |||
| 207 | if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { | ||
| 208 | LOG_ERROR(Service_NFP, "Failed to encode data"); | ||
| 209 | return WriteAmiiboFailed; | ||
| 210 | } | ||
| 211 | |||
| 212 | std::vector<u8> data(sizeof(encrypted_tag_data)); | ||
| 213 | memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); | ||
| 214 | |||
| 215 | if (!npad_device->WriteNfc(data)) { | ||
| 216 | LOG_ERROR(Service_NFP, "Error writing to file"); | ||
| 217 | return WriteAmiiboFailed; | ||
| 218 | } | ||
| 219 | |||
| 220 | is_data_moddified = false; | ||
| 221 | |||
| 222 | return ResultSuccess; | ||
| 223 | } | ||
| 224 | |||
| 225 | Result NfpDevice::Mount(MountTarget mount_target_) { | ||
| 226 | if (device_state != DeviceState::TagFound) { | ||
| 227 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 228 | return WrongDeviceState; | ||
| 229 | } | ||
| 230 | |||
| 231 | if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { | ||
| 232 | LOG_ERROR(Service_NFP, "Not an amiibo"); | ||
| 233 | return NotAnAmiibo; | ||
| 234 | } | ||
| 235 | |||
| 236 | if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { | ||
| 237 | LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); | ||
| 238 | return CorruptedData; | ||
| 239 | } | ||
| 240 | |||
| 241 | device_state = DeviceState::TagMounted; | ||
| 242 | mount_target = mount_target_; | ||
| 243 | return ResultSuccess; | ||
| 244 | } | ||
| 245 | |||
| 246 | Result NfpDevice::Unmount() { | ||
| 247 | if (device_state != DeviceState::TagMounted) { | ||
| 248 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 249 | if (device_state == DeviceState::TagRemoved) { | ||
| 250 | return TagRemoved; | ||
| 251 | } | ||
| 252 | return WrongDeviceState; | ||
| 253 | } | ||
| 254 | |||
| 255 | // Save data before unloading the amiibo | ||
| 256 | if (is_data_moddified) { | ||
| 257 | Flush(); | ||
| 258 | } | ||
| 259 | |||
| 260 | device_state = DeviceState::TagFound; | ||
| 261 | mount_target = MountTarget::None; | ||
| 262 | is_app_area_open = false; | ||
| 263 | |||
| 264 | return ResultSuccess; | ||
| 265 | } | ||
| 266 | |||
| 267 | Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { | ||
| 268 | if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { | ||
| 269 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 270 | if (device_state == DeviceState::TagRemoved) { | ||
| 271 | return TagRemoved; | ||
| 272 | } | ||
| 273 | return WrongDeviceState; | ||
| 274 | } | ||
| 275 | |||
| 276 | tag_info = { | ||
| 277 | .uuid = encrypted_tag_data.uuid.uid, | ||
| 278 | .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()), | ||
| 279 | .protocol = TagProtocol::TypeA, | ||
| 280 | .tag_type = TagType::Type2, | ||
| 281 | }; | ||
| 282 | |||
| 283 | return ResultSuccess; | ||
| 284 | } | ||
| 285 | |||
| 286 | Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { | ||
| 287 | if (device_state != DeviceState::TagMounted) { | ||
| 288 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 289 | if (device_state == DeviceState::TagRemoved) { | ||
| 290 | return TagRemoved; | ||
| 291 | } | ||
| 292 | return WrongDeviceState; | ||
| 293 | } | ||
| 294 | |||
| 295 | if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { | ||
| 296 | LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||
| 297 | return WrongDeviceState; | ||
| 298 | } | ||
| 299 | |||
| 300 | const auto& settings = tag_data.settings; | ||
| 301 | |||
| 302 | // TODO: Validate this data | ||
| 303 | common_info = { | ||
| 304 | .last_write_date = settings.write_date.GetWriteDate(), | ||
| 305 | .write_counter = tag_data.write_counter, | ||
| 306 | .version = 0, | ||
| 307 | .application_area_size = sizeof(ApplicationArea), | ||
| 308 | }; | ||
| 309 | return ResultSuccess; | ||
| 310 | } | ||
| 311 | |||
| 312 | Result NfpDevice::GetModelInfo(ModelInfo& model_info) const { | ||
| 313 | if (device_state != DeviceState::TagMounted) { | ||
| 314 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 315 | if (device_state == DeviceState::TagRemoved) { | ||
| 316 | return TagRemoved; | ||
| 317 | } | ||
| 318 | return WrongDeviceState; | ||
| 319 | } | ||
| 320 | |||
| 321 | const auto& model_info_data = encrypted_tag_data.user_memory.model_info; | ||
| 322 | model_info = { | ||
| 323 | .character_id = model_info_data.character_id, | ||
| 324 | .character_variant = model_info_data.character_variant, | ||
| 325 | .amiibo_type = model_info_data.amiibo_type, | ||
| 326 | .model_number = model_info_data.model_number, | ||
| 327 | .series = model_info_data.series, | ||
| 328 | }; | ||
| 329 | return ResultSuccess; | ||
| 330 | } | ||
| 331 | |||
| 332 | Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const { | ||
| 333 | if (device_state != DeviceState::TagMounted) { | ||
| 334 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 335 | if (device_state == DeviceState::TagRemoved) { | ||
| 336 | return TagRemoved; | ||
| 337 | } | ||
| 338 | return WrongDeviceState; | ||
| 339 | } | ||
| 340 | |||
| 341 | if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { | ||
| 342 | LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||
| 343 | return WrongDeviceState; | ||
| 344 | } | ||
| 345 | |||
| 346 | if (tag_data.settings.settings.amiibo_initialized == 0) { | ||
| 347 | return RegistrationIsNotInitialized; | ||
| 348 | } | ||
| 349 | |||
| 350 | Service::Mii::MiiManager manager; | ||
| 351 | const auto& settings = tag_data.settings; | ||
| 352 | |||
| 353 | // TODO: Validate this data | ||
| 354 | register_info = { | ||
| 355 | .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), | ||
| 356 | .creation_date = settings.init_date.GetWriteDate(), | ||
| 357 | .amiibo_name = GetAmiiboName(settings), | ||
| 358 | .font_region = {}, | ||
| 359 | }; | ||
| 360 | |||
| 361 | return ResultSuccess; | ||
| 362 | } | ||
| 363 | |||
| 364 | Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { | ||
| 365 | if (device_state != DeviceState::TagMounted) { | ||
| 366 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 367 | if (device_state == DeviceState::TagRemoved) { | ||
| 368 | return TagRemoved; | ||
| 369 | } | ||
| 370 | return WrongDeviceState; | ||
| 371 | } | ||
| 372 | |||
| 373 | if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { | ||
| 374 | LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||
| 375 | return WrongDeviceState; | ||
| 376 | } | ||
| 377 | |||
| 378 | Service::Mii::MiiManager manager; | ||
| 379 | auto& settings = tag_data.settings; | ||
| 380 | |||
| 381 | settings.init_date = GetAmiiboDate(current_posix_time); | ||
| 382 | settings.write_date = GetAmiiboDate(current_posix_time); | ||
| 383 | settings.crc_counter++; | ||
| 384 | // TODO: Find how to calculate the crc check | ||
| 385 | // settings.crc = CalculateCRC(settings); | ||
| 386 | |||
| 387 | SetAmiiboName(settings, amiibo_name); | ||
| 388 | tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0)); | ||
| 389 | settings.settings.amiibo_initialized.Assign(1); | ||
| 390 | |||
| 391 | return Flush(); | ||
| 392 | } | ||
| 393 | |||
| 394 | Result NfpDevice::RestoreAmiibo() { | ||
| 395 | if (device_state != DeviceState::TagMounted) { | ||
| 396 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 397 | if (device_state == DeviceState::TagRemoved) { | ||
| 398 | return TagRemoved; | ||
| 399 | } | ||
| 400 | return WrongDeviceState; | ||
| 401 | } | ||
| 402 | |||
| 403 | if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { | ||
| 404 | LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||
| 405 | return WrongDeviceState; | ||
| 406 | } | ||
| 407 | |||
| 408 | // TODO: Load amiibo from backup on system | ||
| 409 | LOG_ERROR(Service_NFP, "Not Implemented"); | ||
| 410 | return ResultSuccess; | ||
| 411 | } | ||
| 412 | |||
| 413 | Result NfpDevice::DeleteAllData() { | ||
| 414 | const auto result = DeleteApplicationArea(); | ||
| 415 | if (result.IsError()) { | ||
| 416 | return result; | ||
| 417 | } | ||
| 418 | |||
| 419 | if (device_state != DeviceState::TagMounted) { | ||
| 420 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 421 | if (device_state == DeviceState::TagRemoved) { | ||
| 422 | return TagRemoved; | ||
| 423 | } | ||
| 424 | return WrongDeviceState; | ||
| 425 | } | ||
| 426 | |||
| 427 | Common::TinyMT rng{}; | ||
| 428 | rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii)); | ||
| 429 | tag_data.settings.settings.amiibo_initialized.Assign(0); | ||
| 430 | |||
| 431 | return Flush(); | ||
| 432 | } | ||
| 433 | |||
| 434 | Result NfpDevice::OpenApplicationArea(u32 access_id) { | ||
| 435 | if (device_state != DeviceState::TagMounted) { | ||
| 436 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 437 | if (device_state == DeviceState::TagRemoved) { | ||
| 438 | return TagRemoved; | ||
| 439 | } | ||
| 440 | return WrongDeviceState; | ||
| 441 | } | ||
| 442 | |||
| 443 | if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { | ||
| 444 | LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||
| 445 | return WrongDeviceState; | ||
| 446 | } | ||
| 447 | |||
| 448 | if (tag_data.settings.settings.appdata_initialized.Value() == 0) { | ||
| 449 | LOG_WARNING(Service_NFP, "Application area is not initialized"); | ||
| 450 | return ApplicationAreaIsNotInitialized; | ||
| 451 | } | ||
| 452 | |||
| 453 | if (tag_data.application_area_id != access_id) { | ||
| 454 | LOG_WARNING(Service_NFP, "Wrong application area id"); | ||
| 455 | return WrongApplicationAreaId; | ||
| 456 | } | ||
| 457 | |||
| 458 | is_app_area_open = true; | ||
| 459 | |||
| 460 | return ResultSuccess; | ||
| 461 | } | ||
| 462 | |||
| 463 | Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const { | ||
| 464 | if (device_state != DeviceState::TagMounted) { | ||
| 465 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 466 | if (device_state == DeviceState::TagRemoved) { | ||
| 467 | return TagRemoved; | ||
| 468 | } | ||
| 469 | return WrongDeviceState; | ||
| 470 | } | ||
| 471 | |||
| 472 | if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { | ||
| 473 | LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||
| 474 | return WrongDeviceState; | ||
| 475 | } | ||
| 476 | |||
| 477 | if (!is_app_area_open) { | ||
| 478 | LOG_ERROR(Service_NFP, "Application area is not open"); | ||
| 479 | return WrongDeviceState; | ||
| 480 | } | ||
| 481 | |||
| 482 | if (tag_data.settings.settings.appdata_initialized.Value() == 0) { | ||
| 483 | LOG_ERROR(Service_NFP, "Application area is not initialized"); | ||
| 484 | return ApplicationAreaIsNotInitialized; | ||
| 485 | } | ||
| 486 | |||
| 487 | if (data.size() > sizeof(ApplicationArea)) { | ||
| 488 | data.resize(sizeof(ApplicationArea)); | ||
| 489 | } | ||
| 490 | |||
| 491 | memcpy(data.data(), tag_data.application_area.data(), data.size()); | ||
| 492 | |||
| 493 | return ResultSuccess; | ||
| 494 | } | ||
| 495 | |||
| 496 | Result NfpDevice::SetApplicationArea(std::span<const u8> data) { | ||
| 497 | if (device_state != DeviceState::TagMounted) { | ||
| 498 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 499 | if (device_state == DeviceState::TagRemoved) { | ||
| 500 | return TagRemoved; | ||
| 501 | } | ||
| 502 | return WrongDeviceState; | ||
| 503 | } | ||
| 504 | |||
| 505 | if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { | ||
| 506 | LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||
| 507 | return WrongDeviceState; | ||
| 508 | } | ||
| 509 | |||
| 510 | if (!is_app_area_open) { | ||
| 511 | LOG_ERROR(Service_NFP, "Application area is not open"); | ||
| 512 | return WrongDeviceState; | ||
| 513 | } | ||
| 514 | |||
| 515 | if (tag_data.settings.settings.appdata_initialized.Value() == 0) { | ||
| 516 | LOG_ERROR(Service_NFP, "Application area is not initialized"); | ||
| 517 | return ApplicationAreaIsNotInitialized; | ||
| 518 | } | ||
| 519 | |||
| 520 | if (data.size() > sizeof(ApplicationArea)) { | ||
| 521 | LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); | ||
| 522 | return ResultUnknown; | ||
| 523 | } | ||
| 524 | |||
| 525 | Common::TinyMT rng{}; | ||
| 526 | std::memcpy(tag_data.application_area.data(), data.data(), data.size()); | ||
| 527 | // Fill remaining data with random numbers | ||
| 528 | rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), | ||
| 529 | sizeof(ApplicationArea) - data.size()); | ||
| 530 | |||
| 531 | tag_data.applicaton_write_counter++; | ||
| 532 | is_data_moddified = true; | ||
| 533 | |||
| 534 | return ResultSuccess; | ||
| 535 | } | ||
| 536 | |||
| 537 | Result NfpDevice::CreateApplicationArea(u32 access_id, std::span<const u8> data) { | ||
| 538 | if (device_state != DeviceState::TagMounted) { | ||
| 539 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 540 | if (device_state == DeviceState::TagRemoved) { | ||
| 541 | return TagRemoved; | ||
| 542 | } | ||
| 543 | return WrongDeviceState; | ||
| 544 | } | ||
| 545 | |||
| 546 | if (tag_data.settings.settings.appdata_initialized.Value() != 0) { | ||
| 547 | LOG_ERROR(Service_NFP, "Application area already exist"); | ||
| 548 | return ApplicationAreaExist; | ||
| 549 | } | ||
| 550 | |||
| 551 | return RecreateApplicationArea(access_id, data); | ||
| 552 | } | ||
| 553 | |||
| 554 | Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> data) { | ||
| 555 | if (device_state != DeviceState::TagMounted) { | ||
| 556 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 557 | if (device_state == DeviceState::TagRemoved) { | ||
| 558 | return TagRemoved; | ||
| 559 | } | ||
| 560 | return WrongDeviceState; | ||
| 561 | } | ||
| 562 | |||
| 563 | if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { | ||
| 564 | LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||
| 565 | return WrongDeviceState; | ||
| 566 | } | ||
| 567 | |||
| 568 | if (data.size() > sizeof(ApplicationArea)) { | ||
| 569 | LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); | ||
| 570 | return WrongApplicationAreaSize; | ||
| 571 | } | ||
| 572 | |||
| 573 | Common::TinyMT rng{}; | ||
| 574 | std::memcpy(tag_data.application_area.data(), data.data(), data.size()); | ||
| 575 | // Fill remaining data with random numbers | ||
| 576 | rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), | ||
| 577 | sizeof(ApplicationArea) - data.size()); | ||
| 578 | |||
| 579 | // TODO: Investigate why the title id needs to be moddified | ||
| 580 | tag_data.title_id = system.GetCurrentProcessProgramID(); | ||
| 581 | tag_data.title_id = tag_data.title_id | 0x30000000ULL; | ||
| 582 | tag_data.settings.settings.appdata_initialized.Assign(1); | ||
| 583 | tag_data.application_area_id = access_id; | ||
| 584 | tag_data.applicaton_write_counter++; | ||
| 585 | tag_data.unknown = {}; | ||
| 586 | |||
| 587 | return Flush(); | ||
| 588 | } | ||
| 589 | |||
| 590 | Result NfpDevice::DeleteApplicationArea() { | ||
| 591 | if (device_state != DeviceState::TagMounted) { | ||
| 592 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 593 | if (device_state == DeviceState::TagRemoved) { | ||
| 594 | return TagRemoved; | ||
| 595 | } | ||
| 596 | return WrongDeviceState; | ||
| 597 | } | ||
| 598 | |||
| 599 | if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { | ||
| 600 | LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||
| 601 | return WrongDeviceState; | ||
| 602 | } | ||
| 603 | |||
| 604 | Common::TinyMT rng{}; | ||
| 605 | rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); | ||
| 606 | rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64)); | ||
| 607 | rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32)); | ||
| 608 | tag_data.settings.settings.appdata_initialized.Assign(0); | ||
| 609 | tag_data.applicaton_write_counter++; | ||
| 610 | tag_data.unknown = {}; | ||
| 611 | |||
| 612 | return Flush(); | ||
| 613 | } | ||
| 614 | |||
| 615 | u64 NfpDevice::GetHandle() const { | ||
| 616 | // Generate a handle based of the npad id | ||
| 617 | return static_cast<u64>(npad_id); | ||
| 618 | } | ||
| 619 | |||
| 620 | u32 NfpDevice::GetApplicationAreaSize() const { | ||
| 621 | return sizeof(ApplicationArea); | ||
| 622 | } | ||
| 623 | |||
| 624 | DeviceState NfpDevice::GetCurrentState() const { | ||
| 625 | return device_state; | ||
| 626 | } | ||
| 627 | |||
| 628 | Core::HID::NpadIdType NfpDevice::GetNpadId() const { | ||
| 629 | return npad_id; | ||
| 630 | } | ||
| 631 | |||
| 632 | AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const { | ||
| 633 | std::array<char16_t, amiibo_name_length> settings_amiibo_name{}; | ||
| 634 | AmiiboName amiibo_name{}; | ||
| 635 | |||
| 636 | // Convert from big endian to little endian | ||
| 637 | for (std::size_t i = 0; i < amiibo_name_length; i++) { | ||
| 638 | settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]); | ||
| 639 | } | ||
| 640 | |||
| 641 | // Convert from utf16 to utf8 | ||
| 642 | const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); | ||
| 643 | memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); | ||
| 644 | |||
| 645 | return amiibo_name; | ||
| 646 | } | ||
| 647 | |||
| 648 | void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) { | ||
| 649 | std::array<char16_t, amiibo_name_length> settings_amiibo_name{}; | ||
| 650 | |||
| 651 | // Convert from utf8 to utf16 | ||
| 652 | const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data()); | ||
| 653 | memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(), | ||
| 654 | amiibo_name_utf16.size() * sizeof(char16_t)); | ||
| 655 | |||
| 656 | // Convert from little endian to big endian | ||
| 657 | for (std::size_t i = 0; i < amiibo_name_length; i++) { | ||
| 658 | settings.amiibo_name[i] = static_cast<u16_be>(settings_amiibo_name[i]); | ||
| 659 | } | ||
| 660 | } | ||
| 661 | |||
| 662 | AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const { | ||
| 663 | const auto& time_zone_manager = | ||
| 664 | system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); | ||
| 665 | Time::TimeZone::CalendarInfo calendar_info{}; | ||
| 666 | AmiiboDate amiibo_date{}; | ||
| 667 | |||
| 668 | amiibo_date.SetYear(2000); | ||
| 669 | amiibo_date.SetMonth(1); | ||
| 670 | amiibo_date.SetDay(1); | ||
| 671 | |||
| 672 | if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { | ||
| 673 | amiibo_date.SetYear(calendar_info.time.year); | ||
| 674 | amiibo_date.SetMonth(calendar_info.time.month); | ||
| 675 | amiibo_date.SetDay(calendar_info.time.day); | ||
| 676 | } | ||
| 677 | |||
| 678 | return amiibo_date; | ||
| 679 | } | ||
| 680 | |||
| 681 | } // namespace Service::NFP | ||
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h new file mode 100644 index 000000000..a5b72cf19 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.h | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "common/common_funcs.h" | ||
| 10 | #include "core/hle/service/kernel_helpers.h" | ||
| 11 | #include "core/hle/service/mii/types.h" | ||
| 12 | #include "core/hle/service/nfp/nfp_types.h" | ||
| 13 | #include "core/hle/service/service.h" | ||
| 14 | |||
| 15 | namespace Kernel { | ||
| 16 | class KEvent; | ||
| 17 | class KReadableEvent; | ||
| 18 | } // namespace Kernel | ||
| 19 | |||
| 20 | namespace Core { | ||
| 21 | class System; | ||
| 22 | } // namespace Core | ||
| 23 | |||
| 24 | namespace Core::HID { | ||
| 25 | class EmulatedController; | ||
| 26 | enum class ControllerTriggerType; | ||
| 27 | enum class NpadIdType : u32; | ||
| 28 | } // namespace Core::HID | ||
| 29 | |||
| 30 | namespace Service::NFP { | ||
| 31 | class NfpDevice { | ||
| 32 | public: | ||
| 33 | NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, | ||
| 34 | KernelHelpers::ServiceContext& service_context_, | ||
| 35 | Kernel::KEvent* availability_change_event_); | ||
| 36 | ~NfpDevice(); | ||
| 37 | |||
| 38 | void Initialize(); | ||
| 39 | void Finalize(); | ||
| 40 | |||
| 41 | Result StartDetection(s32 protocol_); | ||
| 42 | Result StopDetection(); | ||
| 43 | Result Mount(MountTarget mount_target); | ||
| 44 | Result Unmount(); | ||
| 45 | Result Flush(); | ||
| 46 | |||
| 47 | Result GetTagInfo(TagInfo& tag_info) const; | ||
| 48 | Result GetCommonInfo(CommonInfo& common_info) const; | ||
| 49 | Result GetModelInfo(ModelInfo& model_info) const; | ||
| 50 | Result GetRegisterInfo(RegisterInfo& register_info) const; | ||
| 51 | |||
| 52 | Result SetNicknameAndOwner(const AmiiboName& amiibo_name); | ||
| 53 | Result RestoreAmiibo(); | ||
| 54 | Result DeleteAllData(); | ||
| 55 | |||
| 56 | Result OpenApplicationArea(u32 access_id); | ||
| 57 | Result GetApplicationArea(std::vector<u8>& data) const; | ||
| 58 | Result SetApplicationArea(std::span<const u8> data); | ||
| 59 | Result CreateApplicationArea(u32 access_id, std::span<const u8> data); | ||
| 60 | Result RecreateApplicationArea(u32 access_id, std::span<const u8> data); | ||
| 61 | Result DeleteApplicationArea(); | ||
| 62 | |||
| 63 | u64 GetHandle() const; | ||
| 64 | u32 GetApplicationAreaSize() const; | ||
| 65 | DeviceState GetCurrentState() const; | ||
| 66 | Core::HID::NpadIdType GetNpadId() const; | ||
| 67 | |||
| 68 | Kernel::KReadableEvent& GetActivateEvent() const; | ||
| 69 | Kernel::KReadableEvent& GetDeactivateEvent() const; | ||
| 70 | |||
| 71 | private: | ||
| 72 | void NpadUpdate(Core::HID::ControllerTriggerType type); | ||
| 73 | bool LoadAmiibo(std::span<const u8> data); | ||
| 74 | void CloseAmiibo(); | ||
| 75 | |||
| 76 | AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; | ||
| 77 | void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name); | ||
| 78 | AmiiboDate GetAmiiboDate(s64 posix_time) const; | ||
| 79 | |||
| 80 | bool is_controller_set{}; | ||
| 81 | int callback_key; | ||
| 82 | const Core::HID::NpadIdType npad_id; | ||
| 83 | Core::System& system; | ||
| 84 | Core::HID::EmulatedController* npad_device = nullptr; | ||
| 85 | KernelHelpers::ServiceContext& service_context; | ||
| 86 | Kernel::KEvent* activate_event = nullptr; | ||
| 87 | Kernel::KEvent* deactivate_event = nullptr; | ||
| 88 | Kernel::KEvent* availability_change_event = nullptr; | ||
| 89 | |||
| 90 | bool is_data_moddified{}; | ||
| 91 | bool is_app_area_open{}; | ||
| 92 | s32 protocol{}; | ||
| 93 | s64 current_posix_time{}; | ||
| 94 | MountTarget mount_target{MountTarget::None}; | ||
| 95 | DeviceState device_state{DeviceState::Unavailable}; | ||
| 96 | |||
| 97 | NTAG215File tag_data{}; | ||
| 98 | EncryptedNTAG215File encrypted_tag_data{}; | ||
| 99 | }; | ||
| 100 | |||
| 101 | } // namespace Service::NFP | ||
diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h new file mode 100644 index 000000000..d8e4cf094 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_result.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/result.h" | ||
| 7 | |||
| 8 | namespace Service::NFP { | ||
| 9 | |||
| 10 | constexpr Result DeviceNotFound(ErrorModule::NFP, 64); | ||
| 11 | constexpr Result InvalidArgument(ErrorModule::NFP, 65); | ||
| 12 | constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68); | ||
| 13 | constexpr Result WrongDeviceState(ErrorModule::NFP, 73); | ||
| 14 | constexpr Result NfcDisabled(ErrorModule::NFP, 80); | ||
| 15 | constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); | ||
| 16 | constexpr Result TagRemoved(ErrorModule::NFP, 97); | ||
| 17 | constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120); | ||
| 18 | constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); | ||
| 19 | constexpr Result CorruptedData(ErrorModule::NFP, 144); | ||
| 20 | constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); | ||
| 21 | constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); | ||
| 22 | constexpr Result NotAnAmiibo(ErrorModule::NFP, 178); | ||
| 23 | |||
| 24 | } // namespace Service::NFP | ||
diff --git a/src/core/hle/service/nfp/amiibo_types.h b/src/core/hle/service/nfp/nfp_types.h index bf2de811a..867ea2f36 100644 --- a/src/core/hle/service/nfp/amiibo_types.h +++ b/src/core/hle/service/nfp/nfp_types.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | 7 | ||
| 8 | #include "common/swap.h" | ||
| 8 | #include "core/hle/service/mii/types.h" | 9 | #include "core/hle/service/mii/types.h" |
| 9 | 10 | ||
| 10 | namespace Service::NFP { | 11 | namespace Service::NFP { |
| @@ -27,7 +28,7 @@ enum class DeviceState : u32 { | |||
| 27 | TagFound, | 28 | TagFound, |
| 28 | TagRemoved, | 29 | TagRemoved, |
| 29 | TagMounted, | 30 | TagMounted, |
| 30 | Unaviable, | 31 | Unavailable, |
| 31 | Finalized, | 32 | Finalized, |
| 32 | }; | 33 | }; |
| 33 | 34 | ||
| @@ -36,6 +37,7 @@ enum class ModelType : u32 { | |||
| 36 | }; | 37 | }; |
| 37 | 38 | ||
| 38 | enum class MountTarget : u32 { | 39 | enum class MountTarget : u32 { |
| 40 | None, | ||
| 39 | Rom, | 41 | Rom, |
| 40 | Ram, | 42 | Ram, |
| 41 | All, | 43 | All, |
| @@ -73,21 +75,101 @@ enum class AmiiboSeries : u8 { | |||
| 73 | Diablo, | 75 | Diablo, |
| 74 | }; | 76 | }; |
| 75 | 77 | ||
| 76 | using TagUuid = std::array<u8, 10>; | 78 | enum class TagType : u32 { |
| 79 | None, | ||
| 80 | Type1, // ISO14443A RW 96-2k bytes 106kbit/s | ||
| 81 | Type2, // ISO14443A RW/RO 540 bytes 106kbit/s | ||
| 82 | Type3, // Sony Felica RW/RO 2k bytes 212kbit/s | ||
| 83 | Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s | ||
| 84 | Type5, // ISO15693 RW/RO 540 bytes 106kbit/s | ||
| 85 | }; | ||
| 86 | |||
| 87 | enum class PackedTagType : u8 { | ||
| 88 | None, | ||
| 89 | Type1, // ISO14443A RW 96-2k bytes 106kbit/s | ||
| 90 | Type2, // ISO14443A RW/RO 540 bytes 106kbit/s | ||
| 91 | Type3, // Sony Felica RW/RO 2k bytes 212kbit/s | ||
| 92 | Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s | ||
| 93 | Type5, // ISO15693 RW/RO 540 bytes 106kbit/s | ||
| 94 | }; | ||
| 95 | |||
| 96 | enum class TagProtocol : u32 { | ||
| 97 | None, | ||
| 98 | TypeA, // ISO14443A | ||
| 99 | TypeB, // ISO14443B | ||
| 100 | TypeF, // Sony Felica | ||
| 101 | }; | ||
| 102 | |||
| 103 | using UniqueSerialNumber = std::array<u8, 7>; | ||
| 104 | using LockBytes = std::array<u8, 2>; | ||
| 77 | using HashData = std::array<u8, 0x20>; | 105 | using HashData = std::array<u8, 0x20>; |
| 78 | using ApplicationArea = std::array<u8, 0xD8>; | 106 | using ApplicationArea = std::array<u8, 0xD8>; |
| 107 | using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; | ||
| 108 | |||
| 109 | struct TagUuid { | ||
| 110 | UniqueSerialNumber uid; | ||
| 111 | u8 nintendo_id; | ||
| 112 | LockBytes lock_bytes; | ||
| 113 | }; | ||
| 114 | static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); | ||
| 115 | |||
| 116 | struct WriteDate { | ||
| 117 | u16 year; | ||
| 118 | u8 month; | ||
| 119 | u8 day; | ||
| 120 | }; | ||
| 121 | static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); | ||
| 79 | 122 | ||
| 80 | struct AmiiboDate { | 123 | struct AmiiboDate { |
| 81 | u16 raw_date{}; | 124 | u16 raw_date{}; |
| 82 | 125 | ||
| 126 | u16 GetValue() const { | ||
| 127 | return Common::swap16(raw_date); | ||
| 128 | } | ||
| 129 | |||
| 83 | u16 GetYear() const { | 130 | u16 GetYear() const { |
| 84 | return static_cast<u16>(((raw_date & 0xFE00) >> 9) + 2000); | 131 | return static_cast<u16>(((GetValue() & 0xFE00) >> 9) + 2000); |
| 85 | } | 132 | } |
| 86 | u8 GetMonth() const { | 133 | u8 GetMonth() const { |
| 87 | return static_cast<u8>(((raw_date & 0x01E0) >> 5) - 1); | 134 | return static_cast<u8>((GetValue() & 0x01E0) >> 5); |
| 88 | } | 135 | } |
| 89 | u8 GetDay() const { | 136 | u8 GetDay() const { |
| 90 | return static_cast<u8>(raw_date & 0x001F); | 137 | return static_cast<u8>(GetValue() & 0x001F); |
| 138 | } | ||
| 139 | |||
| 140 | WriteDate GetWriteDate() const { | ||
| 141 | if (!IsValidDate()) { | ||
| 142 | return { | ||
| 143 | .year = 2000, | ||
| 144 | .month = 1, | ||
| 145 | .day = 1, | ||
| 146 | }; | ||
| 147 | } | ||
| 148 | return { | ||
| 149 | .year = GetYear(), | ||
| 150 | .month = GetMonth(), | ||
| 151 | .day = GetDay(), | ||
| 152 | }; | ||
| 153 | } | ||
| 154 | |||
| 155 | void SetYear(u16 year) { | ||
| 156 | const u16 year_converted = static_cast<u16>((year - 2000) << 9); | ||
| 157 | raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted); | ||
| 158 | } | ||
| 159 | void SetMonth(u8 month) { | ||
| 160 | const u16 month_converted = static_cast<u16>(month << 5); | ||
| 161 | raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted); | ||
| 162 | } | ||
| 163 | void SetDay(u8 day) { | ||
| 164 | const u16 day_converted = static_cast<u16>(day); | ||
| 165 | raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted); | ||
| 166 | } | ||
| 167 | |||
| 168 | bool IsValidDate() const { | ||
| 169 | const bool is_day_valid = GetDay() > 0 && GetDay() < 32; | ||
| 170 | const bool is_month_valid = GetMonth() >= 0 && GetMonth() < 13; | ||
| 171 | const bool is_year_valid = GetYear() >= 2000; | ||
| 172 | return is_year_valid && is_month_valid && is_day_valid; | ||
| 91 | } | 173 | } |
| 92 | }; | 174 | }; |
| 93 | static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); | 175 | static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); |
| @@ -117,9 +199,9 @@ struct AmiiboModelInfo { | |||
| 117 | u16 character_id; | 199 | u16 character_id; |
| 118 | u8 character_variant; | 200 | u8 character_variant; |
| 119 | AmiiboType amiibo_type; | 201 | AmiiboType amiibo_type; |
| 120 | u16 model_number; | 202 | u16_be model_number; |
| 121 | AmiiboSeries series; | 203 | AmiiboSeries series; |
| 122 | u8 constant_value; // Must be 02 | 204 | PackedTagType tag_type; |
| 123 | INSERT_PADDING_BYTES(0x4); // Unknown | 205 | INSERT_PADDING_BYTES(0x4); // Unknown |
| 124 | }; | 206 | }; |
| 125 | static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); | 207 | static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); |
| @@ -134,7 +216,7 @@ static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid siz | |||
| 134 | #pragma pack(1) | 216 | #pragma pack(1) |
| 135 | struct EncryptedAmiiboFile { | 217 | struct EncryptedAmiiboFile { |
| 136 | u8 constant_value; // Must be A5 | 218 | u8 constant_value; // Must be A5 |
| 137 | u16 write_counter; // Number of times the amiibo has been written? | 219 | u16_be write_counter; // Number of times the amiibo has been written? |
| 138 | INSERT_PADDING_BYTES(0x1); // Unknown 1 | 220 | INSERT_PADDING_BYTES(0x1); // Unknown 1 |
| 139 | AmiiboSettings settings; // Encrypted amiibo settings | 221 | AmiiboSettings settings; // Encrypted amiibo settings |
| 140 | HashData hmac_tag; // Hash | 222 | HashData hmac_tag; // Hash |
| @@ -146,18 +228,18 @@ struct EncryptedAmiiboFile { | |||
| 146 | u16_be applicaton_write_counter; // Encrypted Counter | 228 | u16_be applicaton_write_counter; // Encrypted Counter |
| 147 | u32_be application_area_id; // Encrypted Game id | 229 | u32_be application_area_id; // Encrypted Game id |
| 148 | std::array<u8, 0x2> unknown; | 230 | std::array<u8, 0x2> unknown; |
| 149 | HashData hash; // Probably a SHA256-HMAC hash? | 231 | std::array<u32, 0x8> unknown2; |
| 150 | ApplicationArea application_area; // Encrypted Game data | 232 | ApplicationArea application_area; // Encrypted Game data |
| 151 | }; | 233 | }; |
| 152 | static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); | 234 | static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); |
| 153 | 235 | ||
| 154 | struct NTAG215File { | 236 | struct NTAG215File { |
| 155 | std::array<u8, 0x2> uuid2; | 237 | LockBytes lock_bytes; // Tag UUID |
| 156 | u16 static_lock; // Set defined pages as read only | 238 | u16 static_lock; // Set defined pages as read only |
| 157 | u32 compability_container; // Defines available memory | 239 | u32 compability_container; // Defines available memory |
| 158 | HashData hmac_data; // Hash | 240 | HashData hmac_data; // Hash |
| 159 | u8 constant_value; // Must be A5 | 241 | u8 constant_value; // Must be A5 |
| 160 | u16 write_counter; // Number of times the amiibo has been written? | 242 | u16_be write_counter; // Number of times the amiibo has been written? |
| 161 | INSERT_PADDING_BYTES(0x1); // Unknown 1 | 243 | INSERT_PADDING_BYTES(0x1); // Unknown 1 |
| 162 | AmiiboSettings settings; | 244 | AmiiboSettings settings; |
| 163 | Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data | 245 | Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data |
| @@ -165,10 +247,11 @@ struct NTAG215File { | |||
| 165 | u16_be applicaton_write_counter; // Encrypted Counter | 247 | u16_be applicaton_write_counter; // Encrypted Counter |
| 166 | u32_be application_area_id; | 248 | u32_be application_area_id; |
| 167 | std::array<u8, 0x2> unknown; | 249 | std::array<u8, 0x2> unknown; |
| 168 | HashData hash; // Probably a SHA256-HMAC hash? | 250 | std::array<u32, 0x8> unknown2; |
| 169 | ApplicationArea application_area; // Encrypted Game data | 251 | ApplicationArea application_area; // Encrypted Game data |
| 170 | HashData hmac_tag; // Hash | 252 | HashData hmac_tag; // Hash |
| 171 | std::array<u8, 0x8> uuid; | 253 | UniqueSerialNumber uid; // Unique serial number |
| 254 | u8 nintendo_id; // Tag UUID | ||
| 172 | AmiiboModelInfo model_info; | 255 | AmiiboModelInfo model_info; |
| 173 | HashData keygen_salt; // Salt | 256 | HashData keygen_salt; // Salt |
| 174 | u32 dynamic_lock; // Dynamic lock | 257 | u32 dynamic_lock; // Dynamic lock |
| @@ -194,4 +277,44 @@ static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an | |||
| 194 | static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>, | 277 | static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>, |
| 195 | "EncryptedNTAG215File must be trivially copyable."); | 278 | "EncryptedNTAG215File must be trivially copyable."); |
| 196 | 279 | ||
| 280 | struct TagInfo { | ||
| 281 | UniqueSerialNumber uuid; | ||
| 282 | INSERT_PADDING_BYTES(0x3); | ||
| 283 | u8 uuid_length; | ||
| 284 | INSERT_PADDING_BYTES(0x15); | ||
| 285 | TagProtocol protocol; | ||
| 286 | TagType tag_type; | ||
| 287 | INSERT_PADDING_BYTES(0x30); | ||
| 288 | }; | ||
| 289 | static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); | ||
| 290 | |||
| 291 | struct CommonInfo { | ||
| 292 | WriteDate last_write_date; | ||
| 293 | u16 write_counter; | ||
| 294 | u8 version; | ||
| 295 | INSERT_PADDING_BYTES(0x1); | ||
| 296 | u32 application_area_size; | ||
| 297 | INSERT_PADDING_BYTES(0x34); | ||
| 298 | }; | ||
| 299 | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||
| 300 | |||
| 301 | struct ModelInfo { | ||
| 302 | u16 character_id; | ||
| 303 | u8 character_variant; | ||
| 304 | AmiiboType amiibo_type; | ||
| 305 | u16 model_number; | ||
| 306 | AmiiboSeries series; | ||
| 307 | INSERT_PADDING_BYTES(0x39); // Unknown | ||
| 308 | }; | ||
| 309 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | ||
| 310 | |||
| 311 | struct RegisterInfo { | ||
| 312 | Service::Mii::CharInfo mii_char_info; | ||
| 313 | WriteDate creation_date; | ||
| 314 | AmiiboName amiibo_name; | ||
| 315 | u8 font_region; | ||
| 316 | INSERT_PADDING_BYTES(0x7A); | ||
| 317 | }; | ||
| 318 | static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); | ||
| 319 | |||
| 197 | } // namespace Service::NFP | 320 | } // namespace Service::NFP |
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 2d7b156cf..4ed53b534 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp | |||
| @@ -1,18 +1,674 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <array> | ||
| 5 | #include <atomic> | ||
| 6 | |||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/hid/emulated_controller.h" | ||
| 10 | #include "core/hid/hid_core.h" | ||
| 11 | #include "core/hid/hid_types.h" | ||
| 12 | #include "core/hle/ipc_helpers.h" | ||
| 13 | #include "core/hle/kernel/k_event.h" | ||
| 14 | #include "core/hle/service/mii/mii_manager.h" | ||
| 15 | #include "core/hle/service/nfp/nfp_device.h" | ||
| 16 | #include "core/hle/service/nfp/nfp_result.h" | ||
| 4 | #include "core/hle/service/nfp/nfp_user.h" | 17 | #include "core/hle/service/nfp/nfp_user.h" |
| 5 | 18 | ||
| 6 | namespace Service::NFP { | 19 | namespace Service::NFP { |
| 7 | 20 | ||
| 8 | NFP_User::NFP_User(std::shared_ptr<Module> module_, Core::System& system_) | 21 | IUser::IUser(Core::System& system_) |
| 9 | : Interface(std::move(module_), system_, "nfp:user") { | 22 | : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} { |
| 10 | static const FunctionInfo functions[] = { | 23 | static const FunctionInfo functions[] = { |
| 11 | {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, | 24 | {0, &IUser::Initialize, "Initialize"}, |
| 25 | {1, &IUser::Finalize, "Finalize"}, | ||
| 26 | {2, &IUser::ListDevices, "ListDevices"}, | ||
| 27 | {3, &IUser::StartDetection, "StartDetection"}, | ||
| 28 | {4, &IUser::StopDetection, "StopDetection"}, | ||
| 29 | {5, &IUser::Mount, "Mount"}, | ||
| 30 | {6, &IUser::Unmount, "Unmount"}, | ||
| 31 | {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, | ||
| 32 | {8, &IUser::GetApplicationArea, "GetApplicationArea"}, | ||
| 33 | {9, &IUser::SetApplicationArea, "SetApplicationArea"}, | ||
| 34 | {10, &IUser::Flush, "Flush"}, | ||
| 35 | {11, &IUser::Restore, "Restore"}, | ||
| 36 | {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, | ||
| 37 | {13, &IUser::GetTagInfo, "GetTagInfo"}, | ||
| 38 | {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, | ||
| 39 | {15, &IUser::GetCommonInfo, "GetCommonInfo"}, | ||
| 40 | {16, &IUser::GetModelInfo, "GetModelInfo"}, | ||
| 41 | {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, | ||
| 42 | {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, | ||
| 43 | {19, &IUser::GetState, "GetState"}, | ||
| 44 | {20, &IUser::GetDeviceState, "GetDeviceState"}, | ||
| 45 | {21, &IUser::GetNpadId, "GetNpadId"}, | ||
| 46 | {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, | ||
| 47 | {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, | ||
| 48 | {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"}, | ||
| 12 | }; | 49 | }; |
| 13 | RegisterHandlers(functions); | 50 | RegisterHandlers(functions); |
| 51 | |||
| 52 | availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); | ||
| 53 | |||
| 54 | for (u32 device_index = 0; device_index < 10; device_index++) { | ||
| 55 | devices[device_index] = | ||
| 56 | std::make_shared<NfpDevice>(Core::HID::IndexToNpadIdType(device_index), system, | ||
| 57 | service_context, availability_change_event); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | void IUser::Initialize(Kernel::HLERequestContext& ctx) { | ||
| 62 | LOG_INFO(Service_NFC, "called"); | ||
| 63 | |||
| 64 | state = State::Initialized; | ||
| 65 | |||
| 66 | for (auto& device : devices) { | ||
| 67 | device->Initialize(); | ||
| 68 | } | ||
| 69 | |||
| 70 | IPC::ResponseBuilder rb{ctx, 2, 0}; | ||
| 71 | rb.Push(ResultSuccess); | ||
| 72 | } | ||
| 73 | |||
| 74 | void IUser::Finalize(Kernel::HLERequestContext& ctx) { | ||
| 75 | LOG_INFO(Service_NFP, "called"); | ||
| 76 | |||
| 77 | state = State::NonInitialized; | ||
| 78 | |||
| 79 | for (auto& device : devices) { | ||
| 80 | device->Finalize(); | ||
| 81 | } | ||
| 82 | |||
| 83 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 84 | rb.Push(ResultSuccess); | ||
| 85 | } | ||
| 86 | |||
| 87 | void IUser::ListDevices(Kernel::HLERequestContext& ctx) { | ||
| 88 | LOG_INFO(Service_NFP, "called"); | ||
| 89 | |||
| 90 | if (state == State::NonInitialized) { | ||
| 91 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 92 | rb.Push(NfcDisabled); | ||
| 93 | return; | ||
| 94 | } | ||
| 95 | |||
| 96 | if (!ctx.CanWriteBuffer()) { | ||
| 97 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 98 | rb.Push(InvalidArgument); | ||
| 99 | return; | ||
| 100 | } | ||
| 101 | |||
| 102 | if (ctx.GetWriteBufferSize() == 0) { | ||
| 103 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 104 | rb.Push(InvalidArgument); | ||
| 105 | return; | ||
| 106 | } | ||
| 107 | |||
| 108 | std::vector<u64> nfp_devices; | ||
| 109 | const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64); | ||
| 110 | |||
| 111 | for (auto& device : devices) { | ||
| 112 | if (nfp_devices.size() >= max_allowed_devices) { | ||
| 113 | continue; | ||
| 114 | } | ||
| 115 | if (device->GetCurrentState() != DeviceState::Unavailable) { | ||
| 116 | nfp_devices.push_back(device->GetHandle()); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | if (nfp_devices.size() == 0) { | ||
| 121 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 122 | rb.Push(DeviceNotFound); | ||
| 123 | return; | ||
| 124 | } | ||
| 125 | |||
| 126 | ctx.WriteBuffer(nfp_devices); | ||
| 127 | |||
| 128 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 129 | rb.Push(ResultSuccess); | ||
| 130 | rb.Push(static_cast<s32>(nfp_devices.size())); | ||
| 131 | } | ||
| 132 | |||
| 133 | void IUser::StartDetection(Kernel::HLERequestContext& ctx) { | ||
| 134 | IPC::RequestParser rp{ctx}; | ||
| 135 | const auto device_handle{rp.Pop<u64>()}; | ||
| 136 | const auto nfp_protocol{rp.Pop<s32>()}; | ||
| 137 | LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); | ||
| 138 | |||
| 139 | if (state == State::NonInitialized) { | ||
| 140 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 141 | rb.Push(NfcDisabled); | ||
| 142 | return; | ||
| 143 | } | ||
| 144 | |||
| 145 | auto device = GetNfpDevice(device_handle); | ||
| 146 | |||
| 147 | if (!device.has_value()) { | ||
| 148 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 149 | rb.Push(DeviceNotFound); | ||
| 150 | return; | ||
| 151 | } | ||
| 152 | |||
| 153 | const auto result = device.value()->StartDetection(nfp_protocol); | ||
| 154 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 155 | rb.Push(result); | ||
| 156 | } | ||
| 157 | |||
| 158 | void IUser::StopDetection(Kernel::HLERequestContext& ctx) { | ||
| 159 | IPC::RequestParser rp{ctx}; | ||
| 160 | const auto device_handle{rp.Pop<u64>()}; | ||
| 161 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 162 | |||
| 163 | if (state == State::NonInitialized) { | ||
| 164 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 165 | rb.Push(NfcDisabled); | ||
| 166 | return; | ||
| 167 | } | ||
| 168 | |||
| 169 | auto device = GetNfpDevice(device_handle); | ||
| 170 | |||
| 171 | if (!device.has_value()) { | ||
| 172 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 173 | rb.Push(DeviceNotFound); | ||
| 174 | return; | ||
| 175 | } | ||
| 176 | |||
| 177 | const auto result = device.value()->StopDetection(); | ||
| 178 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 179 | rb.Push(result); | ||
| 180 | } | ||
| 181 | |||
| 182 | void IUser::Mount(Kernel::HLERequestContext& ctx) { | ||
| 183 | IPC::RequestParser rp{ctx}; | ||
| 184 | const auto device_handle{rp.Pop<u64>()}; | ||
| 185 | const auto model_type{rp.PopEnum<ModelType>()}; | ||
| 186 | const auto mount_target{rp.PopEnum<MountTarget>()}; | ||
| 187 | LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, | ||
| 188 | model_type, mount_target); | ||
| 189 | |||
| 190 | if (state == State::NonInitialized) { | ||
| 191 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 192 | rb.Push(NfcDisabled); | ||
| 193 | return; | ||
| 194 | } | ||
| 195 | |||
| 196 | auto device = GetNfpDevice(device_handle); | ||
| 197 | |||
| 198 | if (!device.has_value()) { | ||
| 199 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 200 | rb.Push(DeviceNotFound); | ||
| 201 | return; | ||
| 202 | } | ||
| 203 | |||
| 204 | const auto result = device.value()->Mount(mount_target); | ||
| 205 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 206 | rb.Push(result); | ||
| 207 | } | ||
| 208 | |||
| 209 | void IUser::Unmount(Kernel::HLERequestContext& ctx) { | ||
| 210 | IPC::RequestParser rp{ctx}; | ||
| 211 | const auto device_handle{rp.Pop<u64>()}; | ||
| 212 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 213 | |||
| 214 | if (state == State::NonInitialized) { | ||
| 215 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 216 | rb.Push(NfcDisabled); | ||
| 217 | return; | ||
| 218 | } | ||
| 219 | |||
| 220 | auto device = GetNfpDevice(device_handle); | ||
| 221 | |||
| 222 | if (!device.has_value()) { | ||
| 223 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 224 | rb.Push(DeviceNotFound); | ||
| 225 | return; | ||
| 226 | } | ||
| 227 | |||
| 228 | const auto result = device.value()->Unmount(); | ||
| 229 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 230 | rb.Push(result); | ||
| 231 | } | ||
| 232 | |||
| 233 | void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 234 | IPC::RequestParser rp{ctx}; | ||
| 235 | const auto device_handle{rp.Pop<u64>()}; | ||
| 236 | const auto access_id{rp.Pop<u32>()}; | ||
| 237 | LOG_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id); | ||
| 238 | |||
| 239 | if (state == State::NonInitialized) { | ||
| 240 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 241 | rb.Push(NfcDisabled); | ||
| 242 | return; | ||
| 243 | } | ||
| 244 | |||
| 245 | auto device = GetNfpDevice(device_handle); | ||
| 246 | |||
| 247 | if (!device.has_value()) { | ||
| 248 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 249 | rb.Push(DeviceNotFound); | ||
| 250 | return; | ||
| 251 | } | ||
| 252 | |||
| 253 | const auto result = device.value()->OpenApplicationArea(access_id); | ||
| 254 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 255 | rb.Push(result); | ||
| 256 | } | ||
| 257 | |||
| 258 | void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 259 | IPC::RequestParser rp{ctx}; | ||
| 260 | const auto device_handle{rp.Pop<u64>()}; | ||
| 261 | const auto data_size = ctx.GetWriteBufferSize(); | ||
| 262 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 263 | |||
| 264 | if (state == State::NonInitialized) { | ||
| 265 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 266 | rb.Push(NfcDisabled); | ||
| 267 | return; | ||
| 268 | } | ||
| 269 | |||
| 270 | if (!ctx.CanWriteBuffer()) { | ||
| 271 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 272 | rb.Push(InvalidArgument); | ||
| 273 | return; | ||
| 274 | } | ||
| 275 | |||
| 276 | auto device = GetNfpDevice(device_handle); | ||
| 277 | |||
| 278 | if (!device.has_value()) { | ||
| 279 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 280 | rb.Push(DeviceNotFound); | ||
| 281 | return; | ||
| 282 | } | ||
| 283 | |||
| 284 | std::vector<u8> data(data_size); | ||
| 285 | const auto result = device.value()->GetApplicationArea(data); | ||
| 286 | ctx.WriteBuffer(data); | ||
| 287 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 288 | rb.Push(result); | ||
| 289 | rb.Push(static_cast<u32>(data_size)); | ||
| 290 | } | ||
| 291 | |||
| 292 | void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 293 | IPC::RequestParser rp{ctx}; | ||
| 294 | const auto device_handle{rp.Pop<u64>()}; | ||
| 295 | const auto data{ctx.ReadBuffer()}; | ||
| 296 | LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size()); | ||
| 297 | |||
| 298 | if (state == State::NonInitialized) { | ||
| 299 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 300 | rb.Push(NfcDisabled); | ||
| 301 | return; | ||
| 302 | } | ||
| 303 | |||
| 304 | if (!ctx.CanReadBuffer()) { | ||
| 305 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 306 | rb.Push(InvalidArgument); | ||
| 307 | return; | ||
| 308 | } | ||
| 309 | |||
| 310 | auto device = GetNfpDevice(device_handle); | ||
| 311 | |||
| 312 | if (!device.has_value()) { | ||
| 313 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 314 | rb.Push(DeviceNotFound); | ||
| 315 | return; | ||
| 316 | } | ||
| 317 | |||
| 318 | const auto result = device.value()->SetApplicationArea(data); | ||
| 319 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 320 | rb.Push(result); | ||
| 321 | } | ||
| 322 | |||
| 323 | void IUser::Flush(Kernel::HLERequestContext& ctx) { | ||
| 324 | IPC::RequestParser rp{ctx}; | ||
| 325 | const auto device_handle{rp.Pop<u64>()}; | ||
| 326 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 327 | |||
| 328 | if (state == State::NonInitialized) { | ||
| 329 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 330 | rb.Push(NfcDisabled); | ||
| 331 | return; | ||
| 332 | } | ||
| 333 | |||
| 334 | auto device = GetNfpDevice(device_handle); | ||
| 335 | |||
| 336 | if (!device.has_value()) { | ||
| 337 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 338 | rb.Push(DeviceNotFound); | ||
| 339 | return; | ||
| 340 | } | ||
| 341 | |||
| 342 | const auto result = device.value()->Flush(); | ||
| 343 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 344 | rb.Push(result); | ||
| 345 | } | ||
| 346 | |||
| 347 | void IUser::Restore(Kernel::HLERequestContext& ctx) { | ||
| 348 | IPC::RequestParser rp{ctx}; | ||
| 349 | const auto device_handle{rp.Pop<u64>()}; | ||
| 350 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); | ||
| 351 | |||
| 352 | if (state == State::NonInitialized) { | ||
| 353 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 354 | rb.Push(NfcDisabled); | ||
| 355 | return; | ||
| 356 | } | ||
| 357 | |||
| 358 | auto device = GetNfpDevice(device_handle); | ||
| 359 | |||
| 360 | if (!device.has_value()) { | ||
| 361 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 362 | rb.Push(DeviceNotFound); | ||
| 363 | return; | ||
| 364 | } | ||
| 365 | |||
| 366 | const auto result = device.value()->RestoreAmiibo(); | ||
| 367 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 368 | rb.Push(result); | ||
| 369 | } | ||
| 370 | |||
| 371 | void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 372 | IPC::RequestParser rp{ctx}; | ||
| 373 | const auto device_handle{rp.Pop<u64>()}; | ||
| 374 | const auto access_id{rp.Pop<u32>()}; | ||
| 375 | const auto data{ctx.ReadBuffer()}; | ||
| 376 | LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, | ||
| 377 | access_id, data.size()); | ||
| 378 | |||
| 379 | if (state == State::NonInitialized) { | ||
| 380 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 381 | rb.Push(NfcDisabled); | ||
| 382 | return; | ||
| 383 | } | ||
| 384 | |||
| 385 | if (!ctx.CanReadBuffer()) { | ||
| 386 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 387 | rb.Push(InvalidArgument); | ||
| 388 | return; | ||
| 389 | } | ||
| 390 | |||
| 391 | auto device = GetNfpDevice(device_handle); | ||
| 392 | |||
| 393 | if (!device.has_value()) { | ||
| 394 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 395 | rb.Push(DeviceNotFound); | ||
| 396 | return; | ||
| 397 | } | ||
| 398 | |||
| 399 | const auto result = device.value()->CreateApplicationArea(access_id, data); | ||
| 400 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 401 | rb.Push(result); | ||
| 402 | } | ||
| 403 | |||
| 404 | void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { | ||
| 405 | IPC::RequestParser rp{ctx}; | ||
| 406 | const auto device_handle{rp.Pop<u64>()}; | ||
| 407 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 408 | |||
| 409 | if (state == State::NonInitialized) { | ||
| 410 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 411 | rb.Push(NfcDisabled); | ||
| 412 | return; | ||
| 413 | } | ||
| 414 | |||
| 415 | auto device = GetNfpDevice(device_handle); | ||
| 416 | |||
| 417 | if (!device.has_value()) { | ||
| 418 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 419 | rb.Push(DeviceNotFound); | ||
| 420 | return; | ||
| 421 | } | ||
| 422 | |||
| 423 | TagInfo tag_info{}; | ||
| 424 | const auto result = device.value()->GetTagInfo(tag_info); | ||
| 425 | ctx.WriteBuffer(tag_info); | ||
| 426 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 427 | rb.Push(result); | ||
| 428 | } | ||
| 429 | |||
| 430 | void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) { | ||
| 431 | IPC::RequestParser rp{ctx}; | ||
| 432 | const auto device_handle{rp.Pop<u64>()}; | ||
| 433 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 434 | |||
| 435 | if (state == State::NonInitialized) { | ||
| 436 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 437 | rb.Push(NfcDisabled); | ||
| 438 | return; | ||
| 439 | } | ||
| 440 | |||
| 441 | auto device = GetNfpDevice(device_handle); | ||
| 442 | |||
| 443 | if (!device.has_value()) { | ||
| 444 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 445 | rb.Push(DeviceNotFound); | ||
| 446 | return; | ||
| 447 | } | ||
| 448 | |||
| 449 | RegisterInfo register_info{}; | ||
| 450 | const auto result = device.value()->GetRegisterInfo(register_info); | ||
| 451 | ctx.WriteBuffer(register_info); | ||
| 452 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 453 | rb.Push(result); | ||
| 454 | } | ||
| 455 | |||
| 456 | void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) { | ||
| 457 | IPC::RequestParser rp{ctx}; | ||
| 458 | const auto device_handle{rp.Pop<u64>()}; | ||
| 459 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 460 | |||
| 461 | if (state == State::NonInitialized) { | ||
| 462 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 463 | rb.Push(NfcDisabled); | ||
| 464 | return; | ||
| 465 | } | ||
| 466 | |||
| 467 | auto device = GetNfpDevice(device_handle); | ||
| 468 | |||
| 469 | if (!device.has_value()) { | ||
| 470 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 471 | rb.Push(DeviceNotFound); | ||
| 472 | return; | ||
| 473 | } | ||
| 474 | |||
| 475 | CommonInfo common_info{}; | ||
| 476 | const auto result = device.value()->GetCommonInfo(common_info); | ||
| 477 | ctx.WriteBuffer(common_info); | ||
| 478 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 479 | rb.Push(result); | ||
| 480 | } | ||
| 481 | |||
| 482 | void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) { | ||
| 483 | IPC::RequestParser rp{ctx}; | ||
| 484 | const auto device_handle{rp.Pop<u64>()}; | ||
| 485 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 486 | |||
| 487 | if (state == State::NonInitialized) { | ||
| 488 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 489 | rb.Push(NfcDisabled); | ||
| 490 | return; | ||
| 491 | } | ||
| 492 | |||
| 493 | auto device = GetNfpDevice(device_handle); | ||
| 494 | |||
| 495 | if (!device.has_value()) { | ||
| 496 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 497 | rb.Push(DeviceNotFound); | ||
| 498 | return; | ||
| 499 | } | ||
| 500 | |||
| 501 | ModelInfo model_info{}; | ||
| 502 | const auto result = device.value()->GetModelInfo(model_info); | ||
| 503 | ctx.WriteBuffer(model_info); | ||
| 504 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 505 | rb.Push(result); | ||
| 506 | } | ||
| 507 | |||
| 508 | void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { | ||
| 509 | IPC::RequestParser rp{ctx}; | ||
| 510 | const auto device_handle{rp.Pop<u64>()}; | ||
| 511 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 512 | |||
| 513 | if (state == State::NonInitialized) { | ||
| 514 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 515 | rb.Push(NfcDisabled); | ||
| 516 | return; | ||
| 517 | } | ||
| 518 | |||
| 519 | auto device = GetNfpDevice(device_handle); | ||
| 520 | |||
| 521 | if (!device.has_value()) { | ||
| 522 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 523 | rb.Push(DeviceNotFound); | ||
| 524 | return; | ||
| 525 | } | ||
| 526 | |||
| 527 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 528 | rb.Push(ResultSuccess); | ||
| 529 | rb.PushCopyObjects(device.value()->GetActivateEvent()); | ||
| 530 | } | ||
| 531 | |||
| 532 | void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { | ||
| 533 | IPC::RequestParser rp{ctx}; | ||
| 534 | const auto device_handle{rp.Pop<u64>()}; | ||
| 535 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 536 | |||
| 537 | if (state == State::NonInitialized) { | ||
| 538 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 539 | rb.Push(NfcDisabled); | ||
| 540 | return; | ||
| 541 | } | ||
| 542 | |||
| 543 | auto device = GetNfpDevice(device_handle); | ||
| 544 | |||
| 545 | if (!device.has_value()) { | ||
| 546 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 547 | rb.Push(DeviceNotFound); | ||
| 548 | return; | ||
| 549 | } | ||
| 550 | |||
| 551 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 552 | rb.Push(ResultSuccess); | ||
| 553 | rb.PushCopyObjects(device.value()->GetDeactivateEvent()); | ||
| 554 | } | ||
| 555 | |||
| 556 | void IUser::GetState(Kernel::HLERequestContext& ctx) { | ||
| 557 | LOG_DEBUG(Service_NFC, "called"); | ||
| 558 | |||
| 559 | IPC::ResponseBuilder rb{ctx, 3, 0}; | ||
| 560 | rb.Push(ResultSuccess); | ||
| 561 | rb.PushEnum(state); | ||
| 562 | } | ||
| 563 | |||
| 564 | void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { | ||
| 565 | IPC::RequestParser rp{ctx}; | ||
| 566 | const auto device_handle{rp.Pop<u64>()}; | ||
| 567 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 568 | |||
| 569 | auto device = GetNfpDevice(device_handle); | ||
| 570 | |||
| 571 | if (!device.has_value()) { | ||
| 572 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 573 | rb.Push(DeviceNotFound); | ||
| 574 | return; | ||
| 575 | } | ||
| 576 | |||
| 577 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 578 | rb.Push(ResultSuccess); | ||
| 579 | rb.PushEnum(device.value()->GetCurrentState()); | ||
| 580 | } | ||
| 581 | |||
| 582 | void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { | ||
| 583 | IPC::RequestParser rp{ctx}; | ||
| 584 | const auto device_handle{rp.Pop<u64>()}; | ||
| 585 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 586 | |||
| 587 | if (state == State::NonInitialized) { | ||
| 588 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 589 | rb.Push(NfcDisabled); | ||
| 590 | return; | ||
| 591 | } | ||
| 592 | |||
| 593 | auto device = GetNfpDevice(device_handle); | ||
| 594 | |||
| 595 | if (!device.has_value()) { | ||
| 596 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 597 | rb.Push(DeviceNotFound); | ||
| 598 | return; | ||
| 599 | } | ||
| 600 | |||
| 601 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 602 | rb.Push(ResultSuccess); | ||
| 603 | rb.PushEnum(device.value()->GetNpadId()); | ||
| 604 | } | ||
| 605 | |||
| 606 | void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { | ||
| 607 | IPC::RequestParser rp{ctx}; | ||
| 608 | const auto device_handle{rp.Pop<u64>()}; | ||
| 609 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 610 | |||
| 611 | auto device = GetNfpDevice(device_handle); | ||
| 612 | |||
| 613 | if (!device.has_value()) { | ||
| 614 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 615 | rb.Push(DeviceNotFound); | ||
| 616 | return; | ||
| 617 | } | ||
| 618 | |||
| 619 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 620 | rb.Push(ResultSuccess); | ||
| 621 | rb.Push(device.value()->GetApplicationAreaSize()); | ||
| 14 | } | 622 | } |
| 15 | 623 | ||
| 16 | NFP_User::~NFP_User() = default; | 624 | void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { |
| 625 | LOG_INFO(Service_NFP, "called"); | ||
| 626 | |||
| 627 | if (state == State::NonInitialized) { | ||
| 628 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 629 | rb.Push(NfcDisabled); | ||
| 630 | return; | ||
| 631 | } | ||
| 632 | |||
| 633 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 634 | rb.Push(ResultSuccess); | ||
| 635 | rb.PushCopyObjects(availability_change_event->GetReadableEvent()); | ||
| 636 | } | ||
| 637 | |||
| 638 | void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 639 | IPC::RequestParser rp{ctx}; | ||
| 640 | const auto device_handle{rp.Pop<u64>()}; | ||
| 641 | const auto access_id{rp.Pop<u32>()}; | ||
| 642 | const auto data{ctx.ReadBuffer()}; | ||
| 643 | LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, | ||
| 644 | access_id, data.size()); | ||
| 645 | |||
| 646 | if (state == State::NonInitialized) { | ||
| 647 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 648 | rb.Push(NfcDisabled); | ||
| 649 | return; | ||
| 650 | } | ||
| 651 | |||
| 652 | auto device = GetNfpDevice(device_handle); | ||
| 653 | |||
| 654 | if (!device.has_value()) { | ||
| 655 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 656 | rb.Push(DeviceNotFound); | ||
| 657 | return; | ||
| 658 | } | ||
| 659 | |||
| 660 | const auto result = device.value()->RecreateApplicationArea(access_id, data); | ||
| 661 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 662 | rb.Push(result); | ||
| 663 | } | ||
| 664 | |||
| 665 | std::optional<std::shared_ptr<NfpDevice>> IUser::GetNfpDevice(u64 handle) { | ||
| 666 | for (auto& device : devices) { | ||
| 667 | if (device->GetHandle() == handle) { | ||
| 668 | return device; | ||
| 669 | } | ||
| 670 | } | ||
| 671 | return std::nullopt; | ||
| 672 | } | ||
| 17 | 673 | ||
| 18 | } // namespace Service::NFP | 674 | } // namespace Service::NFP |
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 519ff56ee..68c60ae82 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h | |||
| @@ -3,14 +3,52 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/kernel_helpers.h" | ||
| 6 | #include "core/hle/service/nfp/nfp.h" | 7 | #include "core/hle/service/nfp/nfp.h" |
| 8 | #include "core/hle/service/nfp/nfp_types.h" | ||
| 7 | 9 | ||
| 8 | namespace Service::NFP { | 10 | namespace Service::NFP { |
| 11 | class NfpDevice; | ||
| 9 | 12 | ||
| 10 | class NFP_User final : public Module::Interface { | 13 | class IUser final : public ServiceFramework<IUser> { |
| 11 | public: | 14 | public: |
| 12 | explicit NFP_User(std::shared_ptr<Module> module_, Core::System& system_); | 15 | explicit IUser(Core::System& system_); |
| 13 | ~NFP_User() override; | 16 | |
| 17 | private: | ||
| 18 | void Initialize(Kernel::HLERequestContext& ctx); | ||
| 19 | void Finalize(Kernel::HLERequestContext& ctx); | ||
| 20 | void ListDevices(Kernel::HLERequestContext& ctx); | ||
| 21 | void StartDetection(Kernel::HLERequestContext& ctx); | ||
| 22 | void StopDetection(Kernel::HLERequestContext& ctx); | ||
| 23 | void Mount(Kernel::HLERequestContext& ctx); | ||
| 24 | void Unmount(Kernel::HLERequestContext& ctx); | ||
| 25 | void OpenApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 26 | void GetApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 27 | void SetApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 28 | void Flush(Kernel::HLERequestContext& ctx); | ||
| 29 | void Restore(Kernel::HLERequestContext& ctx); | ||
| 30 | void CreateApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 31 | void GetTagInfo(Kernel::HLERequestContext& ctx); | ||
| 32 | void GetRegisterInfo(Kernel::HLERequestContext& ctx); | ||
| 33 | void GetCommonInfo(Kernel::HLERequestContext& ctx); | ||
| 34 | void GetModelInfo(Kernel::HLERequestContext& ctx); | ||
| 35 | void AttachActivateEvent(Kernel::HLERequestContext& ctx); | ||
| 36 | void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); | ||
| 37 | void GetState(Kernel::HLERequestContext& ctx); | ||
| 38 | void GetDeviceState(Kernel::HLERequestContext& ctx); | ||
| 39 | void GetNpadId(Kernel::HLERequestContext& ctx); | ||
| 40 | void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); | ||
| 41 | void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); | ||
| 42 | void RecreateApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 43 | |||
| 44 | std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle); | ||
| 45 | |||
| 46 | KernelHelpers::ServiceContext service_context; | ||
| 47 | |||
| 48 | std::array<std::shared_ptr<NfpDevice>, 10> devices{}; | ||
| 49 | |||
| 50 | State state{State::NonInitialized}; | ||
| 51 | Kernel::KEvent* availability_change_event; | ||
| 14 | }; | 52 | }; |
| 15 | 53 | ||
| 16 | } // namespace Service::NFP | 54 | } // namespace Service::NFP |
diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp new file mode 100644 index 000000000..37ca24f5d --- /dev/null +++ b/src/core/hle/service/nvdrv/core/container.cpp | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2022 yuzu Emulator Project | ||
| 2 | // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors | ||
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 4 | |||
| 5 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 6 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 7 | #include "core/hle/service/nvdrv/core/syncpoint_manager.h" | ||
| 8 | #include "video_core/host1x/host1x.h" | ||
| 9 | |||
| 10 | namespace Service::Nvidia::NvCore { | ||
| 11 | |||
| 12 | struct ContainerImpl { | ||
| 13 | explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_) | ||
| 14 | : file{host1x_}, manager{host1x_}, device_file_data{} {} | ||
| 15 | NvMap file; | ||
| 16 | SyncpointManager manager; | ||
| 17 | Container::Host1xDeviceFileData device_file_data; | ||
| 18 | }; | ||
| 19 | |||
| 20 | Container::Container(Tegra::Host1x::Host1x& host1x_) { | ||
| 21 | impl = std::make_unique<ContainerImpl>(host1x_); | ||
| 22 | } | ||
| 23 | |||
| 24 | Container::~Container() = default; | ||
| 25 | |||
| 26 | NvMap& Container::GetNvMapFile() { | ||
| 27 | return impl->file; | ||
| 28 | } | ||
| 29 | |||
| 30 | const NvMap& Container::GetNvMapFile() const { | ||
| 31 | return impl->file; | ||
| 32 | } | ||
| 33 | |||
| 34 | Container::Host1xDeviceFileData& Container::Host1xDeviceFile() { | ||
| 35 | return impl->device_file_data; | ||
| 36 | } | ||
| 37 | |||
| 38 | const Container::Host1xDeviceFileData& Container::Host1xDeviceFile() const { | ||
| 39 | return impl->device_file_data; | ||
| 40 | } | ||
| 41 | |||
| 42 | SyncpointManager& Container::GetSyncpointManager() { | ||
| 43 | return impl->manager; | ||
| 44 | } | ||
| 45 | |||
| 46 | const SyncpointManager& Container::GetSyncpointManager() const { | ||
| 47 | return impl->manager; | ||
| 48 | } | ||
| 49 | |||
| 50 | } // namespace Service::Nvidia::NvCore | ||
diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h new file mode 100644 index 000000000..b4b63ac90 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/container.h | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2022 yuzu Emulator Project | ||
| 2 | // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors | ||
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <deque> | ||
| 8 | #include <memory> | ||
| 9 | #include <unordered_map> | ||
| 10 | |||
| 11 | #include "core/hle/service/nvdrv/nvdata.h" | ||
| 12 | |||
| 13 | namespace Tegra::Host1x { | ||
| 14 | class Host1x; | ||
| 15 | } // namespace Tegra::Host1x | ||
| 16 | |||
| 17 | namespace Service::Nvidia::NvCore { | ||
| 18 | |||
| 19 | class NvMap; | ||
| 20 | class SyncpointManager; | ||
| 21 | |||
| 22 | struct ContainerImpl; | ||
| 23 | |||
| 24 | class Container { | ||
| 25 | public: | ||
| 26 | explicit Container(Tegra::Host1x::Host1x& host1x); | ||
| 27 | ~Container(); | ||
| 28 | |||
| 29 | NvMap& GetNvMapFile(); | ||
| 30 | |||
| 31 | const NvMap& GetNvMapFile() const; | ||
| 32 | |||
| 33 | SyncpointManager& GetSyncpointManager(); | ||
| 34 | |||
| 35 | const SyncpointManager& GetSyncpointManager() const; | ||
| 36 | |||
| 37 | struct Host1xDeviceFileData { | ||
| 38 | std::unordered_map<DeviceFD, u32> fd_to_id{}; | ||
| 39 | std::deque<u32> syncpts_accumulated{}; | ||
| 40 | u32 nvdec_next_id{}; | ||
| 41 | u32 vic_next_id{}; | ||
| 42 | }; | ||
| 43 | |||
| 44 | Host1xDeviceFileData& Host1xDeviceFile(); | ||
| 45 | |||
| 46 | const Host1xDeviceFileData& Host1xDeviceFile() const; | ||
| 47 | |||
| 48 | private: | ||
| 49 | std::unique_ptr<ContainerImpl> impl; | ||
| 50 | }; | ||
| 51 | |||
| 52 | } // namespace Service::Nvidia::NvCore | ||
diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp new file mode 100644 index 000000000..fbd8a74a5 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp | |||
| @@ -0,0 +1,272 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2022 yuzu Emulator Project | ||
| 2 | // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors | ||
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 4 | |||
| 5 | #include "common/alignment.h" | ||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 9 | #include "core/memory.h" | ||
| 10 | #include "video_core/host1x/host1x.h" | ||
| 11 | |||
| 12 | using Core::Memory::YUZU_PAGESIZE; | ||
| 13 | |||
| 14 | namespace Service::Nvidia::NvCore { | ||
| 15 | NvMap::Handle::Handle(u64 size_, Id id_) | ||
| 16 | : size(size_), aligned_size(size), orig_size(size), id(id_) { | ||
| 17 | flags.raw = 0; | ||
| 18 | } | ||
| 19 | |||
| 20 | NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) { | ||
| 21 | std::scoped_lock lock(mutex); | ||
| 22 | |||
| 23 | // Handles cannot be allocated twice | ||
| 24 | if (allocated) { | ||
| 25 | return NvResult::AccessDenied; | ||
| 26 | } | ||
| 27 | |||
| 28 | flags = pFlags; | ||
| 29 | kind = pKind; | ||
| 30 | align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign; | ||
| 31 | |||
| 32 | // This flag is only applicable for handles with an address passed | ||
| 33 | if (pAddress) { | ||
| 34 | flags.keep_uncached_after_free.Assign(0); | ||
| 35 | } else { | ||
| 36 | LOG_CRITICAL(Service_NVDRV, | ||
| 37 | "Mapping nvmap handles without a CPU side address is unimplemented!"); | ||
| 38 | } | ||
| 39 | |||
| 40 | size = Common::AlignUp(size, YUZU_PAGESIZE); | ||
| 41 | aligned_size = Common::AlignUp(size, align); | ||
| 42 | address = pAddress; | ||
| 43 | allocated = true; | ||
| 44 | |||
| 45 | return NvResult::Success; | ||
| 46 | } | ||
| 47 | |||
| 48 | NvResult NvMap::Handle::Duplicate(bool internal_session) { | ||
| 49 | std::scoped_lock lock(mutex); | ||
| 50 | // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS) | ||
| 51 | if (!allocated) [[unlikely]] { | ||
| 52 | return NvResult::BadValue; | ||
| 53 | } | ||
| 54 | |||
| 55 | // If we internally use FromId the duplication tracking of handles won't work accurately due to | ||
| 56 | // us not implementing per-process handle refs. | ||
| 57 | if (internal_session) { | ||
| 58 | internal_dupes++; | ||
| 59 | } else { | ||
| 60 | dupes++; | ||
| 61 | } | ||
| 62 | |||
| 63 | return NvResult::Success; | ||
| 64 | } | ||
| 65 | |||
| 66 | NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {} | ||
| 67 | |||
| 68 | void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) { | ||
| 69 | std::scoped_lock lock(handles_lock); | ||
| 70 | |||
| 71 | handles.emplace(handle_description->id, std::move(handle_description)); | ||
| 72 | } | ||
| 73 | |||
| 74 | void NvMap::UnmapHandle(Handle& handle_description) { | ||
| 75 | // Remove pending unmap queue entry if needed | ||
| 76 | if (handle_description.unmap_queue_entry) { | ||
| 77 | unmap_queue.erase(*handle_description.unmap_queue_entry); | ||
| 78 | handle_description.unmap_queue_entry.reset(); | ||
| 79 | } | ||
| 80 | |||
| 81 | // Free and unmap the handle from the SMMU | ||
| 82 | host1x.MemoryManager().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address), | ||
| 83 | handle_description.aligned_size); | ||
| 84 | host1x.Allocator().Free(handle_description.pin_virt_address, | ||
| 85 | static_cast<u32>(handle_description.aligned_size)); | ||
| 86 | handle_description.pin_virt_address = 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | bool NvMap::TryRemoveHandle(const Handle& handle_description) { | ||
| 90 | // No dupes left, we can remove from handle map | ||
| 91 | if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) { | ||
| 92 | std::scoped_lock lock(handles_lock); | ||
| 93 | |||
| 94 | auto it{handles.find(handle_description.id)}; | ||
| 95 | if (it != handles.end()) { | ||
| 96 | handles.erase(it); | ||
| 97 | } | ||
| 98 | |||
| 99 | return true; | ||
| 100 | } else { | ||
| 101 | return false; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | NvResult NvMap::CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out) { | ||
| 106 | if (!size) [[unlikely]] { | ||
| 107 | return NvResult::BadValue; | ||
| 108 | } | ||
| 109 | |||
| 110 | u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)}; | ||
| 111 | auto handle_description{std::make_shared<Handle>(size, id)}; | ||
| 112 | AddHandle(handle_description); | ||
| 113 | |||
| 114 | result_out = handle_description; | ||
| 115 | return NvResult::Success; | ||
| 116 | } | ||
| 117 | |||
| 118 | std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) { | ||
| 119 | std::scoped_lock lock(handles_lock); | ||
| 120 | try { | ||
| 121 | return handles.at(handle); | ||
| 122 | } catch (std::out_of_range&) { | ||
| 123 | return nullptr; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | VAddr NvMap::GetHandleAddress(Handle::Id handle) { | ||
| 128 | std::scoped_lock lock(handles_lock); | ||
| 129 | try { | ||
| 130 | return handles.at(handle)->address; | ||
| 131 | } catch (std::out_of_range&) { | ||
| 132 | return 0; | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | u32 NvMap::PinHandle(NvMap::Handle::Id handle) { | ||
| 137 | auto handle_description{GetHandle(handle)}; | ||
| 138 | if (!handle_description) [[unlikely]] { | ||
| 139 | return 0; | ||
| 140 | } | ||
| 141 | |||
| 142 | std::scoped_lock lock(handle_description->mutex); | ||
| 143 | if (!handle_description->pins) { | ||
| 144 | // If we're in the unmap queue we can just remove ourselves and return since we're already | ||
| 145 | // mapped | ||
| 146 | { | ||
| 147 | // Lock now to prevent our queue entry from being removed for allocation in-between the | ||
| 148 | // following check and erase | ||
| 149 | std::scoped_lock queueLock(unmap_queue_lock); | ||
| 150 | if (handle_description->unmap_queue_entry) { | ||
| 151 | unmap_queue.erase(*handle_description->unmap_queue_entry); | ||
| 152 | handle_description->unmap_queue_entry.reset(); | ||
| 153 | |||
| 154 | handle_description->pins++; | ||
| 155 | return handle_description->pin_virt_address; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | // If not then allocate some space and map it | ||
| 160 | u32 address{}; | ||
| 161 | auto& smmu_allocator = host1x.Allocator(); | ||
| 162 | auto& smmu_memory_manager = host1x.MemoryManager(); | ||
| 163 | while (!(address = | ||
| 164 | smmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)))) { | ||
| 165 | // Free handles until the allocation succeeds | ||
| 166 | std::scoped_lock queueLock(unmap_queue_lock); | ||
| 167 | if (auto freeHandleDesc{unmap_queue.front()}) { | ||
| 168 | // Handles in the unmap queue are guaranteed not to be pinned so don't bother | ||
| 169 | // checking if they are before unmapping | ||
| 170 | std::scoped_lock freeLock(freeHandleDesc->mutex); | ||
| 171 | if (handle_description->pin_virt_address) | ||
| 172 | UnmapHandle(*freeHandleDesc); | ||
| 173 | } else { | ||
| 174 | LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | smmu_memory_manager.Map(static_cast<GPUVAddr>(address), handle_description->address, | ||
| 179 | handle_description->aligned_size); | ||
| 180 | handle_description->pin_virt_address = address; | ||
| 181 | } | ||
| 182 | |||
| 183 | handle_description->pins++; | ||
| 184 | return handle_description->pin_virt_address; | ||
| 185 | } | ||
| 186 | |||
| 187 | void NvMap::UnpinHandle(Handle::Id handle) { | ||
| 188 | auto handle_description{GetHandle(handle)}; | ||
| 189 | if (!handle_description) { | ||
| 190 | return; | ||
| 191 | } | ||
| 192 | |||
| 193 | std::scoped_lock lock(handle_description->mutex); | ||
| 194 | if (--handle_description->pins < 0) { | ||
| 195 | LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!"); | ||
| 196 | } else if (!handle_description->pins) { | ||
| 197 | std::scoped_lock queueLock(unmap_queue_lock); | ||
| 198 | |||
| 199 | // Add to the unmap queue allowing this handle's memory to be freed if needed | ||
| 200 | unmap_queue.push_back(handle_description); | ||
| 201 | handle_description->unmap_queue_entry = std::prev(unmap_queue.end()); | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) { | ||
| 206 | auto handle_description{GetHandle(handle)}; | ||
| 207 | if (!handle_description) { | ||
| 208 | LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); | ||
| 209 | return; | ||
| 210 | } | ||
| 211 | |||
| 212 | auto result = handle_description->Duplicate(internal_session); | ||
| 213 | if (result != NvResult::Success) { | ||
| 214 | LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internal_session) { | ||
| 219 | std::weak_ptr<Handle> hWeak{GetHandle(handle)}; | ||
| 220 | FreeInfo freeInfo; | ||
| 221 | |||
| 222 | // We use a weak ptr here so we can tell when the handle has been freed and report that back to | ||
| 223 | // guest | ||
| 224 | if (auto handle_description = hWeak.lock()) { | ||
| 225 | std::scoped_lock lock(handle_description->mutex); | ||
| 226 | |||
| 227 | if (internal_session) { | ||
| 228 | if (--handle_description->internal_dupes < 0) | ||
| 229 | LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!"); | ||
| 230 | } else { | ||
| 231 | if (--handle_description->dupes < 0) { | ||
| 232 | LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!"); | ||
| 233 | } else if (handle_description->dupes == 0) { | ||
| 234 | // Force unmap the handle | ||
| 235 | if (handle_description->pin_virt_address) { | ||
| 236 | std::scoped_lock queueLock(unmap_queue_lock); | ||
| 237 | UnmapHandle(*handle_description); | ||
| 238 | } | ||
| 239 | |||
| 240 | handle_description->pins = 0; | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | // Try to remove the shared ptr to the handle from the map, if nothing else is using the | ||
| 245 | // handle then it will now be freed when `handle_description` goes out of scope | ||
| 246 | if (TryRemoveHandle(*handle_description)) { | ||
| 247 | LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle); | ||
| 248 | } else { | ||
| 249 | LOG_DEBUG(Service_NVDRV, | ||
| 250 | "Tried to free nvmap handle: {} but didn't as it still has duplicates", | ||
| 251 | handle); | ||
| 252 | } | ||
| 253 | |||
| 254 | freeInfo = { | ||
| 255 | .address = handle_description->address, | ||
| 256 | .size = handle_description->size, | ||
| 257 | .was_uncached = handle_description->flags.map_uncached.Value() != 0, | ||
| 258 | }; | ||
| 259 | } else { | ||
| 260 | return std::nullopt; | ||
| 261 | } | ||
| 262 | |||
| 263 | // Handle hasn't been freed from memory, set address to 0 to mark that the handle wasn't freed | ||
| 264 | if (!hWeak.expired()) { | ||
| 265 | LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); | ||
| 266 | freeInfo.address = 0; | ||
| 267 | } | ||
| 268 | |||
| 269 | return freeInfo; | ||
| 270 | } | ||
| 271 | |||
| 272 | } // namespace Service::Nvidia::NvCore | ||
diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h new file mode 100644 index 000000000..b9dd3801f --- /dev/null +++ b/src/core/hle/service/nvdrv/core/nvmap.h | |||
| @@ -0,0 +1,175 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2022 yuzu Emulator Project | ||
| 2 | // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors | ||
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <list> | ||
| 9 | #include <memory> | ||
| 10 | #include <mutex> | ||
| 11 | #include <optional> | ||
| 12 | #include <unordered_map> | ||
| 13 | #include <assert.h> | ||
| 14 | |||
| 15 | #include "common/bit_field.h" | ||
| 16 | #include "common/common_types.h" | ||
| 17 | #include "core/hle/service/nvdrv/nvdata.h" | ||
| 18 | |||
| 19 | namespace Tegra { | ||
| 20 | |||
| 21 | namespace Host1x { | ||
| 22 | class Host1x; | ||
| 23 | } // namespace Host1x | ||
| 24 | |||
| 25 | } // namespace Tegra | ||
| 26 | |||
| 27 | namespace Service::Nvidia::NvCore { | ||
| 28 | /** | ||
| 29 | * @brief The nvmap core class holds the global state for nvmap and provides methods to manage | ||
| 30 | * handles | ||
| 31 | */ | ||
| 32 | class NvMap { | ||
| 33 | public: | ||
| 34 | /** | ||
| 35 | * @brief A handle to a contiguous block of memory in an application's address space | ||
| 36 | */ | ||
| 37 | struct Handle { | ||
| 38 | std::mutex mutex; | ||
| 39 | |||
| 40 | u64 align{}; //!< The alignment to use when pinning the handle onto the SMMU | ||
| 41 | u64 size; //!< Page-aligned size of the memory the handle refers to | ||
| 42 | u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to | ||
| 43 | u64 orig_size; //!< Original unaligned size of the memory this handle refers to | ||
| 44 | |||
| 45 | s32 dupes{1}; //!< How many guest references there are to this handle | ||
| 46 | s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle | ||
| 47 | |||
| 48 | using Id = u32; | ||
| 49 | Id id; //!< A globally unique identifier for this handle | ||
| 50 | |||
| 51 | s32 pins{}; | ||
| 52 | u32 pin_virt_address{}; | ||
| 53 | std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{}; | ||
| 54 | |||
| 55 | union Flags { | ||
| 56 | u32 raw; | ||
| 57 | BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached | ||
| 58 | BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was | ||
| 59 | //!< allocated with a fixed address | ||
| 60 | BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins | ||
| 61 | } flags{}; | ||
| 62 | static_assert(sizeof(Flags) == sizeof(u32)); | ||
| 63 | |||
| 64 | u64 address{}; //!< The memory location in the guest's AS that this handle corresponds to, | ||
| 65 | //!< this can also be in the nvdrv tmem | ||
| 66 | bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC | ||
| 67 | //!< call | ||
| 68 | |||
| 69 | u8 kind{}; //!< Used for memory compression | ||
| 70 | bool allocated{}; //!< If the handle has been allocated with `Alloc` | ||
| 71 | |||
| 72 | u64 dma_map_addr{}; //! remove me after implementing pinning. | ||
| 73 | |||
| 74 | Handle(u64 size, Id id); | ||
| 75 | |||
| 76 | /** | ||
| 77 | * @brief Sets up the handle with the given memory config, can allocate memory from the tmem | ||
| 78 | * if a 0 address is passed | ||
| 79 | */ | ||
| 80 | [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress); | ||
| 81 | |||
| 82 | /** | ||
| 83 | * @brief Increases the dupe counter of the handle for the given session | ||
| 84 | */ | ||
| 85 | [[nodiscard]] NvResult Duplicate(bool internal_session); | ||
| 86 | |||
| 87 | /** | ||
| 88 | * @brief Obtains a pointer to the handle's memory and marks the handle it as having been | ||
| 89 | * mapped | ||
| 90 | */ | ||
| 91 | u8* GetPointer() { | ||
| 92 | if (!address) { | ||
| 93 | return nullptr; | ||
| 94 | } | ||
| 95 | |||
| 96 | is_shared_mem_mapped = true; | ||
| 97 | return reinterpret_cast<u8*>(address); | ||
| 98 | } | ||
| 99 | }; | ||
| 100 | |||
| 101 | /** | ||
| 102 | * @brief Encapsulates the result of a FreeHandle operation | ||
| 103 | */ | ||
| 104 | struct FreeInfo { | ||
| 105 | u64 address; //!< Address the handle referred to before deletion | ||
| 106 | u64 size; //!< Page-aligned handle size | ||
| 107 | bool was_uncached; //!< If the handle was allocated as uncached | ||
| 108 | }; | ||
| 109 | |||
| 110 | explicit NvMap(Tegra::Host1x::Host1x& host1x); | ||
| 111 | |||
| 112 | /** | ||
| 113 | * @brief Creates an unallocated handle of the given size | ||
| 114 | */ | ||
| 115 | [[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out); | ||
| 116 | |||
| 117 | std::shared_ptr<Handle> GetHandle(Handle::Id handle); | ||
| 118 | |||
| 119 | VAddr GetHandleAddress(Handle::Id handle); | ||
| 120 | |||
| 121 | /** | ||
| 122 | * @brief Maps a handle into the SMMU address space | ||
| 123 | * @note This operation is refcounted, the number of calls to this must eventually match the | ||
| 124 | * number of calls to `UnpinHandle` | ||
| 125 | * @return The SMMU virtual address that the handle has been mapped to | ||
| 126 | */ | ||
| 127 | u32 PinHandle(Handle::Id handle); | ||
| 128 | |||
| 129 | /** | ||
| 130 | * @brief When this has been called an equal number of times to `PinHandle` for the supplied | ||
| 131 | * handle it will be added to a list of handles to be freed when necessary | ||
| 132 | */ | ||
| 133 | void UnpinHandle(Handle::Id handle); | ||
| 134 | |||
| 135 | /** | ||
| 136 | * @brief Tries to duplicate a handle | ||
| 137 | */ | ||
| 138 | void DuplicateHandle(Handle::Id handle, bool internal_session = false); | ||
| 139 | |||
| 140 | /** | ||
| 141 | * @brief Tries to free a handle and remove a single dupe | ||
| 142 | * @note If a handle has no dupes left and has no other users a FreeInfo struct will be returned | ||
| 143 | * describing the prior state of the handle | ||
| 144 | */ | ||
| 145 | std::optional<FreeInfo> FreeHandle(Handle::Id handle, bool internal_session); | ||
| 146 | |||
| 147 | private: | ||
| 148 | std::list<std::shared_ptr<Handle>> unmap_queue{}; | ||
| 149 | std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue` | ||
| 150 | |||
| 151 | std::unordered_map<Handle::Id, std::shared_ptr<Handle>> | ||
| 152 | handles{}; //!< Main owning map of handles | ||
| 153 | std::mutex handles_lock; //!< Protects access to `handles` | ||
| 154 | |||
| 155 | static constexpr u32 HandleIdIncrement{ | ||
| 156 | 4}; //!< Each new handle ID is an increment of 4 from the previous | ||
| 157 | std::atomic<u32> next_handle_id{HandleIdIncrement}; | ||
| 158 | Tegra::Host1x::Host1x& host1x; | ||
| 159 | |||
| 160 | void AddHandle(std::shared_ptr<Handle> handle); | ||
| 161 | |||
| 162 | /** | ||
| 163 | * @brief Unmaps and frees the SMMU memory region a handle is mapped to | ||
| 164 | * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this | ||
| 165 | */ | ||
| 166 | void UnmapHandle(Handle& handle_description); | ||
| 167 | |||
| 168 | /** | ||
| 169 | * @brief Removes a handle from the map taking its dupes into account | ||
| 170 | * @note handle_description.mutex MUST be locked when calling this | ||
| 171 | * @return If the handle was removed from the map | ||
| 172 | */ | ||
| 173 | bool TryRemoveHandle(const Handle& handle_description); | ||
| 174 | }; | ||
| 175 | } // namespace Service::Nvidia::NvCore | ||
diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp new file mode 100644 index 000000000..eda2041a0 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp | |||
| @@ -0,0 +1,121 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2022 yuzu Emulator Project | ||
| 2 | // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors | ||
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "core/hle/service/nvdrv/core/syncpoint_manager.h" | ||
| 7 | #include "video_core/host1x/host1x.h" | ||
| 8 | |||
| 9 | namespace Service::Nvidia::NvCore { | ||
| 10 | |||
| 11 | SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} { | ||
| 12 | constexpr u32 VBlank0SyncpointId{26}; | ||
| 13 | constexpr u32 VBlank1SyncpointId{27}; | ||
| 14 | |||
| 15 | // Reserve both vblank syncpoints as client managed as they use Continuous Mode | ||
| 16 | // Refer to section 14.3.5.3 of the TRM for more information on Continuous Mode | ||
| 17 | // https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/drm/dc.c#L660 | ||
| 18 | ReserveSyncpoint(VBlank0SyncpointId, true); | ||
| 19 | ReserveSyncpoint(VBlank1SyncpointId, true); | ||
| 20 | |||
| 21 | for (u32 syncpoint_id : channel_syncpoints) { | ||
| 22 | if (syncpoint_id) { | ||
| 23 | ReserveSyncpoint(syncpoint_id, false); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | SyncpointManager::~SyncpointManager() = default; | ||
| 29 | |||
| 30 | u32 SyncpointManager::ReserveSyncpoint(u32 id, bool client_managed) { | ||
| 31 | if (syncpoints.at(id).reserved) { | ||
| 32 | ASSERT_MSG(false, "Requested syncpoint is in use"); | ||
| 33 | return 0; | ||
| 34 | } | ||
| 35 | |||
| 36 | syncpoints.at(id).reserved = true; | ||
| 37 | syncpoints.at(id).interface_managed = client_managed; | ||
| 38 | |||
| 39 | return id; | ||
| 40 | } | ||
| 41 | |||
| 42 | u32 SyncpointManager::FindFreeSyncpoint() { | ||
| 43 | for (u32 i{1}; i < syncpoints.size(); i++) { | ||
| 44 | if (!syncpoints[i].reserved) { | ||
| 45 | return i; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | ASSERT_MSG(false, "Failed to find a free syncpoint!"); | ||
| 49 | return 0; | ||
| 50 | } | ||
| 51 | |||
| 52 | u32 SyncpointManager::AllocateSyncpoint(bool client_managed) { | ||
| 53 | std::lock_guard lock(reservation_lock); | ||
| 54 | return ReserveSyncpoint(FindFreeSyncpoint(), client_managed); | ||
| 55 | } | ||
| 56 | |||
| 57 | void SyncpointManager::FreeSyncpoint(u32 id) { | ||
| 58 | std::lock_guard lock(reservation_lock); | ||
| 59 | ASSERT(syncpoints.at(id).reserved); | ||
| 60 | syncpoints.at(id).reserved = false; | ||
| 61 | } | ||
| 62 | |||
| 63 | bool SyncpointManager::IsSyncpointAllocated(u32 id) { | ||
| 64 | return (id <= SyncpointCount) && syncpoints[id].reserved; | ||
| 65 | } | ||
| 66 | |||
| 67 | bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) const { | ||
| 68 | const SyncpointInfo& syncpoint{syncpoints.at(id)}; | ||
| 69 | |||
| 70 | if (!syncpoint.reserved) { | ||
| 71 | ASSERT(false); | ||
| 72 | return 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | // If the interface manages counters then we don't keep track of the maximum value as it handles | ||
| 76 | // sanity checking the values then | ||
| 77 | if (syncpoint.interface_managed) { | ||
| 78 | return static_cast<s32>(syncpoint.counter_min - threshold) >= 0; | ||
| 79 | } else { | ||
| 80 | return (syncpoint.counter_max - threshold) >= (syncpoint.counter_min - threshold); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) { | ||
| 85 | if (!syncpoints.at(id).reserved) { | ||
| 86 | ASSERT(false); | ||
| 87 | return 0; | ||
| 88 | } | ||
| 89 | |||
| 90 | return syncpoints.at(id).counter_max += amount; | ||
| 91 | } | ||
| 92 | |||
| 93 | u32 SyncpointManager::ReadSyncpointMinValue(u32 id) { | ||
| 94 | if (!syncpoints.at(id).reserved) { | ||
| 95 | ASSERT(false); | ||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 99 | return syncpoints.at(id).counter_min; | ||
| 100 | } | ||
| 101 | |||
| 102 | u32 SyncpointManager::UpdateMin(u32 id) { | ||
| 103 | if (!syncpoints.at(id).reserved) { | ||
| 104 | ASSERT(false); | ||
| 105 | return 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | syncpoints.at(id).counter_min = host1x.GetSyncpointManager().GetHostSyncpointValue(id); | ||
| 109 | return syncpoints.at(id).counter_min; | ||
| 110 | } | ||
| 111 | |||
| 112 | NvFence SyncpointManager::GetSyncpointFence(u32 id) { | ||
| 113 | if (!syncpoints.at(id).reserved) { | ||
| 114 | ASSERT(false); | ||
| 115 | return NvFence{}; | ||
| 116 | } | ||
| 117 | |||
| 118 | return {.id = static_cast<s32>(id), .value = syncpoints.at(id).counter_max}; | ||
| 119 | } | ||
| 120 | |||
| 121 | } // namespace Service::Nvidia::NvCore | ||
diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h new file mode 100644 index 000000000..b76ef9032 --- /dev/null +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2022 yuzu Emulator Project | ||
| 2 | // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors | ||
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <atomic> | ||
| 9 | #include <mutex> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "core/hle/service/nvdrv/nvdata.h" | ||
| 13 | |||
| 14 | namespace Tegra::Host1x { | ||
| 15 | class Host1x; | ||
| 16 | } // namespace Tegra::Host1x | ||
| 17 | |||
| 18 | namespace Service::Nvidia::NvCore { | ||
| 19 | |||
| 20 | enum class ChannelType : u32 { | ||
| 21 | MsEnc = 0, | ||
| 22 | VIC = 1, | ||
| 23 | GPU = 2, | ||
| 24 | NvDec = 3, | ||
| 25 | Display = 4, | ||
| 26 | NvJpg = 5, | ||
| 27 | TSec = 6, | ||
| 28 | Max = 7 | ||
| 29 | }; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * @brief SyncpointManager handles allocating and accessing host1x syncpoints, these are cached | ||
| 33 | * versions of the HW syncpoints which are intermittently synced | ||
| 34 | * @note Refer to Chapter 14 of the Tegra X1 TRM for an exhaustive overview of them | ||
| 35 | * @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html | ||
| 36 | * @url | ||
| 37 | * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c | ||
| 38 | */ | ||
| 39 | class SyncpointManager final { | ||
| 40 | public: | ||
| 41 | explicit SyncpointManager(Tegra::Host1x::Host1x& host1x); | ||
| 42 | ~SyncpointManager(); | ||
| 43 | |||
| 44 | /** | ||
| 45 | * @brief Checks if the given syncpoint is both allocated and below the number of HW syncpoints | ||
| 46 | */ | ||
| 47 | bool IsSyncpointAllocated(u32 id); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * @brief Finds a free syncpoint and reserves it | ||
| 51 | * @return The ID of the reserved syncpoint | ||
| 52 | */ | ||
| 53 | u32 AllocateSyncpoint(bool client_managed); | ||
| 54 | |||
| 55 | /** | ||
| 56 | * @url | ||
| 57 | * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/syncpt.c#L259 | ||
| 58 | */ | ||
| 59 | bool HasSyncpointExpired(u32 id, u32 threshold) const; | ||
| 60 | |||
| 61 | bool IsFenceSignalled(NvFence fence) const { | ||
| 62 | return HasSyncpointExpired(fence.id, fence.value); | ||
| 63 | } | ||
| 64 | |||
| 65 | /** | ||
| 66 | * @brief Atomically increments the maximum value of a syncpoint by the given amount | ||
| 67 | * @return The new max value of the syncpoint | ||
| 68 | */ | ||
| 69 | u32 IncrementSyncpointMaxExt(u32 id, u32 amount); | ||
| 70 | |||
| 71 | /** | ||
| 72 | * @return The minimum value of the syncpoint | ||
| 73 | */ | ||
| 74 | u32 ReadSyncpointMinValue(u32 id); | ||
| 75 | |||
| 76 | /** | ||
| 77 | * @brief Synchronises the minimum value of the syncpoint to with the GPU | ||
| 78 | * @return The new minimum value of the syncpoint | ||
| 79 | */ | ||
| 80 | u32 UpdateMin(u32 id); | ||
| 81 | |||
| 82 | /** | ||
| 83 | * @brief Frees the usage of a syncpoint. | ||
| 84 | */ | ||
| 85 | void FreeSyncpoint(u32 id); | ||
| 86 | |||
| 87 | /** | ||
| 88 | * @return A fence that will be signalled once this syncpoint hits its maximum value | ||
| 89 | */ | ||
| 90 | NvFence GetSyncpointFence(u32 id); | ||
| 91 | |||
| 92 | static constexpr std::array<u32, static_cast<u32>(ChannelType::Max)> channel_syncpoints{ | ||
| 93 | 0x0, // `MsEnc` is unimplemented | ||
| 94 | 0xC, // `VIC` | ||
| 95 | 0x0, // `GPU` syncpoints are allocated per-channel instead | ||
| 96 | 0x36, // `NvDec` | ||
| 97 | 0x0, // `Display` is unimplemented | ||
| 98 | 0x37, // `NvJpg` | ||
| 99 | 0x0, // `TSec` is unimplemented | ||
| 100 | }; //!< Maps each channel ID to a constant syncpoint | ||
| 101 | |||
| 102 | private: | ||
| 103 | /** | ||
| 104 | * @note reservation_lock should be locked when calling this | ||
| 105 | */ | ||
| 106 | u32 ReserveSyncpoint(u32 id, bool client_managed); | ||
| 107 | |||
| 108 | /** | ||
| 109 | * @return The ID of the first free syncpoint | ||
| 110 | */ | ||
| 111 | u32 FindFreeSyncpoint(); | ||
| 112 | |||
| 113 | struct SyncpointInfo { | ||
| 114 | std::atomic<u32> counter_min; //!< The least value the syncpoint can be (The value it was | ||
| 115 | //!< when it was last synchronized with host1x) | ||
| 116 | std::atomic<u32> counter_max; //!< The maximum value the syncpoint can reach according to | ||
| 117 | //!< the current usage | ||
| 118 | bool interface_managed; //!< If the syncpoint is managed by a host1x client interface, a | ||
| 119 | //!< client interface is a HW block that can handle host1x | ||
| 120 | //!< transactions on behalf of a host1x client (Which would | ||
| 121 | //!< otherwise need to be manually synced using PIO which is | ||
| 122 | //!< synchronous and requires direct cooperation of the CPU) | ||
| 123 | bool reserved; //!< If the syncpoint is reserved or not, not to be confused with a reserved | ||
| 124 | //!< value | ||
| 125 | }; | ||
| 126 | |||
| 127 | constexpr static std::size_t SyncpointCount{192}; | ||
| 128 | std::array<SyncpointInfo, SyncpointCount> syncpoints{}; | ||
| 129 | std::mutex reservation_lock; | ||
| 130 | |||
| 131 | Tegra::Host1x::Host1x& host1x; | ||
| 132 | }; | ||
| 133 | |||
| 134 | } // namespace Service::Nvidia::NvCore | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 696e8121e..204b0e757 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h | |||
| @@ -11,6 +11,10 @@ namespace Core { | |||
| 11 | class System; | 11 | class System; |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | namespace Kernel { | ||
| 15 | class KEvent; | ||
| 16 | } | ||
| 17 | |||
| 14 | namespace Service::Nvidia::Devices { | 18 | namespace Service::Nvidia::Devices { |
| 15 | 19 | ||
| 16 | /// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to | 20 | /// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to |
| @@ -64,6 +68,10 @@ public: | |||
| 64 | */ | 68 | */ |
| 65 | virtual void OnClose(DeviceFD fd) = 0; | 69 | virtual void OnClose(DeviceFD fd) = 0; |
| 66 | 70 | ||
| 71 | virtual Kernel::KEvent* QueryEvent(u32 event_id) { | ||
| 72 | return nullptr; | ||
| 73 | } | ||
| 74 | |||
| 67 | protected: | 75 | protected: |
| 68 | Core::System& system; | 76 | Core::System& system; |
| 69 | }; | 77 | }; |
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 604711914..4122fc98d 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | |||
| @@ -5,15 +5,16 @@ | |||
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 9 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 8 | #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" | 10 | #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" |
| 9 | #include "core/hle/service/nvdrv/devices/nvmap.h" | ||
| 10 | #include "core/perf_stats.h" | 11 | #include "core/perf_stats.h" |
| 11 | #include "video_core/gpu.h" | 12 | #include "video_core/gpu.h" |
| 12 | 13 | ||
| 13 | namespace Service::Nvidia::Devices { | 14 | namespace Service::Nvidia::Devices { |
| 14 | 15 | ||
| 15 | nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) | 16 | nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core) |
| 16 | : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} | 17 | : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} |
| 17 | nvdisp_disp0::~nvdisp_disp0() = default; | 18 | nvdisp_disp0::~nvdisp_disp0() = default; |
| 18 | 19 | ||
| 19 | NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 20 | NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -39,8 +40,9 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {} | |||
| 39 | 40 | ||
| 40 | void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, | 41 | void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, |
| 41 | u32 height, u32 stride, android::BufferTransformFlags transform, | 42 | u32 height, u32 stride, android::BufferTransformFlags transform, |
| 42 | const Common::Rectangle<int>& crop_rect) { | 43 | const Common::Rectangle<int>& crop_rect, |
| 43 | const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); | 44 | std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) { |
| 45 | const VAddr addr = nvmap.GetHandleAddress(buffer_handle); | ||
| 44 | LOG_TRACE(Service, | 46 | LOG_TRACE(Service, |
| 45 | "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", | 47 | "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", |
| 46 | addr, offset, width, height, stride, format); | 48 | addr, offset, width, height, stride, format); |
| @@ -48,10 +50,15 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form | |||
| 48 | const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, | 50 | const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, |
| 49 | stride, format, transform, crop_rect}; | 51 | stride, format, transform, crop_rect}; |
| 50 | 52 | ||
| 53 | system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); | ||
| 51 | system.GetPerfStats().EndSystemFrame(); | 54 | system.GetPerfStats().EndSystemFrame(); |
| 52 | system.GPU().SwapBuffers(&framebuffer); | ||
| 53 | system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); | 55 | system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); |
| 54 | system.GetPerfStats().BeginSystemFrame(); | 56 | system.GetPerfStats().BeginSystemFrame(); |
| 55 | } | 57 | } |
| 56 | 58 | ||
| 59 | Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) { | ||
| 60 | LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id); | ||
| 61 | return nullptr; | ||
| 62 | } | ||
| 63 | |||
| 57 | } // namespace Service::Nvidia::Devices | 64 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 67b105e02..04217ab12 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h | |||
| @@ -11,13 +11,18 @@ | |||
| 11 | #include "core/hle/service/nvflinger/buffer_transform_flags.h" | 11 | #include "core/hle/service/nvflinger/buffer_transform_flags.h" |
| 12 | #include "core/hle/service/nvflinger/pixel_format.h" | 12 | #include "core/hle/service/nvflinger/pixel_format.h" |
| 13 | 13 | ||
| 14 | namespace Service::Nvidia::NvCore { | ||
| 15 | class Container; | ||
| 16 | class NvMap; | ||
| 17 | } // namespace Service::Nvidia::NvCore | ||
| 18 | |||
| 14 | namespace Service::Nvidia::Devices { | 19 | namespace Service::Nvidia::Devices { |
| 15 | 20 | ||
| 16 | class nvmap; | 21 | class nvmap; |
| 17 | 22 | ||
| 18 | class nvdisp_disp0 final : public nvdevice { | 23 | class nvdisp_disp0 final : public nvdevice { |
| 19 | public: | 24 | public: |
| 20 | explicit nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_); | 25 | explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core); |
| 21 | ~nvdisp_disp0() override; | 26 | ~nvdisp_disp0() override; |
| 22 | 27 | ||
| 23 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 28 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -33,10 +38,14 @@ public: | |||
| 33 | /// Performs a screen flip, drawing the buffer pointed to by the handle. | 38 | /// Performs a screen flip, drawing the buffer pointed to by the handle. |
| 34 | void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, | 39 | void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, |
| 35 | u32 stride, android::BufferTransformFlags transform, | 40 | u32 stride, android::BufferTransformFlags transform, |
| 36 | const Common::Rectangle<int>& crop_rect); | 41 | const Common::Rectangle<int>& crop_rect, |
| 42 | std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences); | ||
| 43 | |||
| 44 | Kernel::KEvent* QueryEvent(u32 event_id) override; | ||
| 37 | 45 | ||
| 38 | private: | 46 | private: |
| 39 | std::shared_ptr<nvmap> nvmap_dev; | 47 | NvCore::Container& container; |
| 48 | NvCore::NvMap& nvmap; | ||
| 40 | }; | 49 | }; |
| 41 | 50 | ||
| 42 | } // namespace Service::Nvidia::Devices | 51 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 9867a648d..6411dbf43 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -1,21 +1,30 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors |
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | 4 | ||
| 4 | #include <cstring> | 5 | #include <cstring> |
| 5 | #include <utility> | 6 | #include <utility> |
| 6 | 7 | ||
| 8 | #include "common/alignment.h" | ||
| 7 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 13 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 10 | #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" | 14 | #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" |
| 11 | #include "core/hle/service/nvdrv/devices/nvmap.h" | 15 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" |
| 16 | #include "core/hle/service/nvdrv/nvdrv.h" | ||
| 17 | #include "video_core/control/channel_state.h" | ||
| 18 | #include "video_core/gpu.h" | ||
| 12 | #include "video_core/memory_manager.h" | 19 | #include "video_core/memory_manager.h" |
| 13 | #include "video_core/rasterizer_interface.h" | 20 | #include "video_core/rasterizer_interface.h" |
| 14 | 21 | ||
| 15 | namespace Service::Nvidia::Devices { | 22 | namespace Service::Nvidia::Devices { |
| 16 | 23 | ||
| 17 | nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) | 24 | nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core) |
| 18 | : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} | 25 | : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{}, |
| 26 | gmmu{} {} | ||
| 27 | |||
| 19 | nvhost_as_gpu::~nvhost_as_gpu() = default; | 28 | nvhost_as_gpu::~nvhost_as_gpu() = default; |
| 20 | 29 | ||
| 21 | NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 30 | NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -82,12 +91,52 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>& | |||
| 82 | IoctlAllocAsEx params{}; | 91 | IoctlAllocAsEx params{}; |
| 83 | std::memcpy(¶ms, input.data(), input.size()); | 92 | std::memcpy(¶ms, input.data(), input.size()); |
| 84 | 93 | ||
| 85 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); | 94 | LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size); |
| 86 | if (params.big_page_size == 0) { | 95 | |
| 87 | params.big_page_size = DEFAULT_BIG_PAGE_SIZE; | 96 | std::scoped_lock lock(mutex); |
| 97 | |||
| 98 | if (vm.initialised) { | ||
| 99 | ASSERT_MSG(false, "Cannot initialise an address space twice!"); | ||
| 100 | return NvResult::InvalidState; | ||
| 101 | } | ||
| 102 | |||
| 103 | if (params.big_page_size) { | ||
| 104 | if (!std::has_single_bit(params.big_page_size)) { | ||
| 105 | LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: 0x{:X}!", params.big_page_size); | ||
| 106 | return NvResult::BadValue; | ||
| 107 | } | ||
| 108 | |||
| 109 | if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) { | ||
| 110 | LOG_ERROR(Service_NVDRV, "Unsupported big page size: 0x{:X}!", params.big_page_size); | ||
| 111 | return NvResult::BadValue; | ||
| 112 | } | ||
| 113 | |||
| 114 | vm.big_page_size = params.big_page_size; | ||
| 115 | vm.big_page_size_bits = static_cast<u32>(std::countr_zero(params.big_page_size)); | ||
| 116 | |||
| 117 | vm.va_range_start = params.big_page_size << VM::VA_START_SHIFT; | ||
| 118 | } | ||
| 119 | |||
| 120 | // If this is unspecified then default values should be used | ||
| 121 | if (params.va_range_start) { | ||
| 122 | vm.va_range_start = params.va_range_start; | ||
| 123 | vm.va_range_split = params.va_range_split; | ||
| 124 | vm.va_range_end = params.va_range_end; | ||
| 88 | } | 125 | } |
| 89 | 126 | ||
| 90 | big_page_size = params.big_page_size; | 127 | const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; |
| 128 | const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; | ||
| 129 | vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages); | ||
| 130 | |||
| 131 | const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)}; | ||
| 132 | const auto end_big_pages{ | ||
| 133 | static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; | ||
| 134 | vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages); | ||
| 135 | |||
| 136 | gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits, | ||
| 137 | VM::PAGE_SIZE_BITS); | ||
| 138 | system.GPU().InitAddressSpace(*gmmu); | ||
| 139 | vm.initialised = true; | ||
| 91 | 140 | ||
| 92 | return NvResult::Success; | 141 | return NvResult::Success; |
| 93 | } | 142 | } |
| @@ -99,21 +148,76 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector< | |||
| 99 | LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, | 148 | LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, |
| 100 | params.page_size, params.flags); | 149 | params.page_size, params.flags); |
| 101 | 150 | ||
| 102 | const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; | 151 | std::scoped_lock lock(mutex); |
| 103 | if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) { | 152 | |
| 104 | params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size); | 153 | if (!vm.initialised) { |
| 154 | return NvResult::BadValue; | ||
| 155 | } | ||
| 156 | |||
| 157 | if (params.page_size != VM::YUZU_PAGESIZE && params.page_size != vm.big_page_size) { | ||
| 158 | return NvResult::BadValue; | ||
| 159 | } | ||
| 160 | |||
| 161 | if (params.page_size != vm.big_page_size && | ||
| 162 | ((params.flags & MappingFlags::Sparse) != MappingFlags::None)) { | ||
| 163 | UNIMPLEMENTED_MSG("Sparse small pages are not implemented!"); | ||
| 164 | return NvResult::NotImplemented; | ||
| 165 | } | ||
| 166 | |||
| 167 | const u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS | ||
| 168 | : vm.big_page_size_bits}; | ||
| 169 | |||
| 170 | auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator | ||
| 171 | : *vm.big_page_allocator}; | ||
| 172 | |||
| 173 | if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) { | ||
| 174 | allocator.AllocateFixed(static_cast<u32>(params.offset >> page_size_bits), params.pages); | ||
| 105 | } else { | 175 | } else { |
| 106 | params.offset = system.GPU().MemoryManager().Allocate(size, params.align); | 176 | params.offset = static_cast<u64>(allocator.Allocate(params.pages)) << page_size_bits; |
| 177 | if (!params.offset) { | ||
| 178 | ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); | ||
| 179 | return NvResult::InsufficientMemory; | ||
| 180 | } | ||
| 107 | } | 181 | } |
| 108 | 182 | ||
| 109 | auto result = NvResult::Success; | 183 | u64 size{static_cast<u64>(params.pages) * params.page_size}; |
| 110 | if (!params.offset) { | 184 | |
| 111 | LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); | 185 | if ((params.flags & MappingFlags::Sparse) != MappingFlags::None) { |
| 112 | result = NvResult::InsufficientMemory; | 186 | gmmu->MapSparse(params.offset, size); |
| 113 | } | 187 | } |
| 114 | 188 | ||
| 189 | allocation_map[params.offset] = { | ||
| 190 | .size = size, | ||
| 191 | .mappings{}, | ||
| 192 | .page_size = params.page_size, | ||
| 193 | .sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None, | ||
| 194 | .big_pages = params.page_size != VM::YUZU_PAGESIZE, | ||
| 195 | }; | ||
| 196 | |||
| 115 | std::memcpy(output.data(), ¶ms, output.size()); | 197 | std::memcpy(output.data(), ¶ms, output.size()); |
| 116 | return result; | 198 | return NvResult::Success; |
| 199 | } | ||
| 200 | |||
| 201 | void nvhost_as_gpu::FreeMappingLocked(u64 offset) { | ||
| 202 | auto mapping{mapping_map.at(offset)}; | ||
| 203 | |||
| 204 | if (!mapping->fixed) { | ||
| 205 | auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; | ||
| 206 | u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; | ||
| 207 | |||
| 208 | allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits), | ||
| 209 | static_cast<u32>(mapping->size >> page_size_bits)); | ||
| 210 | } | ||
| 211 | |||
| 212 | // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state | ||
| 213 | // Only FreeSpace can unmap them fully | ||
| 214 | if (mapping->sparse_alloc) { | ||
| 215 | gmmu->MapSparse(offset, mapping->size, mapping->big_page); | ||
| 216 | } else { | ||
| 217 | gmmu->Unmap(offset, mapping->size); | ||
| 218 | } | ||
| 219 | |||
| 220 | mapping_map.erase(offset); | ||
| 117 | } | 221 | } |
| 118 | 222 | ||
| 119 | NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { | 223 | NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { |
| @@ -123,8 +227,40 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& | |||
| 123 | LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, | 227 | LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, |
| 124 | params.pages, params.page_size); | 228 | params.pages, params.page_size); |
| 125 | 229 | ||
| 126 | system.GPU().MemoryManager().Unmap(params.offset, | 230 | std::scoped_lock lock(mutex); |
| 127 | static_cast<std::size_t>(params.pages) * params.page_size); | 231 | |
| 232 | if (!vm.initialised) { | ||
| 233 | return NvResult::BadValue; | ||
| 234 | } | ||
| 235 | |||
| 236 | try { | ||
| 237 | auto allocation{allocation_map[params.offset]}; | ||
| 238 | |||
| 239 | if (allocation.page_size != params.page_size || | ||
| 240 | allocation.size != (static_cast<u64>(params.pages) * params.page_size)) { | ||
| 241 | return NvResult::BadValue; | ||
| 242 | } | ||
| 243 | |||
| 244 | for (const auto& mapping : allocation.mappings) { | ||
| 245 | FreeMappingLocked(mapping->offset); | ||
| 246 | } | ||
| 247 | |||
| 248 | // Unset sparse flag if required | ||
| 249 | if (allocation.sparse) { | ||
| 250 | gmmu->Unmap(params.offset, allocation.size); | ||
| 251 | } | ||
| 252 | |||
| 253 | auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator | ||
| 254 | : *vm.big_page_allocator}; | ||
| 255 | u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS | ||
| 256 | : vm.big_page_size_bits}; | ||
| 257 | |||
| 258 | allocator.Free(static_cast<u32>(params.offset >> page_size_bits), | ||
| 259 | static_cast<u32>(allocation.size >> page_size_bits)); | ||
| 260 | allocation_map.erase(params.offset); | ||
| 261 | } catch (const std::out_of_range&) { | ||
| 262 | return NvResult::BadValue; | ||
| 263 | } | ||
| 128 | 264 | ||
| 129 | std::memcpy(output.data(), ¶ms, output.size()); | 265 | std::memcpy(output.data(), ¶ms, output.size()); |
| 130 | return NvResult::Success; | 266 | return NvResult::Success; |
| @@ -135,35 +271,52 @@ NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& out | |||
| 135 | 271 | ||
| 136 | LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); | 272 | LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); |
| 137 | 273 | ||
| 138 | auto result = NvResult::Success; | ||
| 139 | std::vector<IoctlRemapEntry> entries(num_entries); | 274 | std::vector<IoctlRemapEntry> entries(num_entries); |
| 140 | std::memcpy(entries.data(), input.data(), input.size()); | 275 | std::memcpy(entries.data(), input.data(), input.size()); |
| 141 | 276 | ||
| 277 | std::scoped_lock lock(mutex); | ||
| 278 | |||
| 279 | if (!vm.initialised) { | ||
| 280 | return NvResult::BadValue; | ||
| 281 | } | ||
| 282 | |||
| 142 | for (const auto& entry : entries) { | 283 | for (const auto& entry : entries) { |
| 143 | LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", | 284 | GPUVAddr virtual_address{static_cast<u64>(entry.as_offset_big_pages) |
| 144 | entry.offset, entry.nvmap_handle, entry.pages); | 285 | << vm.big_page_size_bits}; |
| 286 | u64 size{static_cast<u64>(entry.big_pages) << vm.big_page_size_bits}; | ||
| 145 | 287 | ||
| 146 | const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; | 288 | auto alloc{allocation_map.upper_bound(virtual_address)}; |
| 147 | if (!object) { | 289 | |
| 148 | LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); | 290 | if (alloc-- == allocation_map.begin() || |
| 149 | result = NvResult::InvalidState; | 291 | (virtual_address - alloc->first) + size > alloc->second.size) { |
| 150 | break; | 292 | LOG_WARNING(Service_NVDRV, "Cannot remap into an unallocated region!"); |
| 293 | return NvResult::BadValue; | ||
| 151 | } | 294 | } |
| 152 | 295 | ||
| 153 | const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10}; | 296 | if (!alloc->second.sparse) { |
| 154 | const auto size{static_cast<u64>(entry.pages) << 0x10}; | 297 | LOG_WARNING(Service_NVDRV, "Cannot remap a non-sparse mapping!"); |
| 155 | const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10}; | 298 | return NvResult::BadValue; |
| 156 | const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)}; | 299 | } |
| 157 | 300 | ||
| 158 | if (!addr) { | 301 | const bool use_big_pages = alloc->second.big_pages; |
| 159 | LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); | 302 | if (!entry.handle) { |
| 160 | result = NvResult::InvalidState; | 303 | gmmu->MapSparse(virtual_address, size, use_big_pages); |
| 161 | break; | 304 | } else { |
| 305 | auto handle{nvmap.GetHandle(entry.handle)}; | ||
| 306 | if (!handle) { | ||
| 307 | return NvResult::BadValue; | ||
| 308 | } | ||
| 309 | |||
| 310 | VAddr cpu_address{static_cast<VAddr>( | ||
| 311 | handle->address + | ||
| 312 | (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; | ||
| 313 | |||
| 314 | gmmu->Map(virtual_address, cpu_address, size, use_big_pages); | ||
| 162 | } | 315 | } |
| 163 | } | 316 | } |
| 164 | 317 | ||
| 165 | std::memcpy(output.data(), entries.data(), output.size()); | 318 | std::memcpy(output.data(), entries.data(), output.size()); |
| 166 | return result; | 319 | return NvResult::Success; |
| 167 | } | 320 | } |
| 168 | 321 | ||
| 169 | NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | 322 | NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { |
| @@ -173,79 +326,98 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8 | |||
| 173 | LOG_DEBUG(Service_NVDRV, | 326 | LOG_DEBUG(Service_NVDRV, |
| 174 | "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" | 327 | "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" |
| 175 | ", offset={}", | 328 | ", offset={}", |
| 176 | params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, | 329 | params.flags, params.handle, params.buffer_offset, params.mapping_size, |
| 177 | params.offset); | 330 | params.offset); |
| 178 | 331 | ||
| 179 | const auto object{nvmap_dev->GetObject(params.nvmap_handle)}; | 332 | std::scoped_lock lock(mutex); |
| 180 | if (!object) { | ||
| 181 | LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); | ||
| 182 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 183 | return NvResult::InvalidState; | ||
| 184 | } | ||
| 185 | |||
| 186 | // The real nvservices doesn't make a distinction between handles and ids, and | ||
| 187 | // object can only have one handle and it will be the same as its id. Assert that this is the | ||
| 188 | // case to prevent unexpected behavior. | ||
| 189 | ASSERT(object->id == params.nvmap_handle); | ||
| 190 | auto& gpu = system.GPU(); | ||
| 191 | 333 | ||
| 192 | u64 page_size{params.page_size}; | 334 | if (!vm.initialised) { |
| 193 | if (!page_size) { | 335 | return NvResult::BadValue; |
| 194 | page_size = object->align; | ||
| 195 | } | 336 | } |
| 196 | 337 | ||
| 197 | if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) { | 338 | // Remaps a subregion of an existing mapping to a different PA |
| 198 | if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) { | 339 | if ((params.flags & MappingFlags::Remap) != MappingFlags::None) { |
| 199 | const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)}; | 340 | try { |
| 200 | const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)}; | 341 | auto mapping{mapping_map.at(params.offset)}; |
| 201 | 342 | ||
| 202 | if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) { | 343 | if (mapping->size < params.mapping_size) { |
| 203 | LOG_CRITICAL(Service_NVDRV, | 344 | LOG_WARNING(Service_NVDRV, |
| 204 | "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, " | 345 | "Cannot remap a partially mapped GPU address space region: 0x{:X}", |
| 205 | "mapping_size = {}, offset={}", | 346 | params.offset); |
| 206 | params.flags, params.nvmap_handle, params.buffer_offset, | 347 | return NvResult::BadValue; |
| 207 | params.mapping_size, params.offset); | ||
| 208 | |||
| 209 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 210 | return NvResult::InvalidState; | ||
| 211 | } | 348 | } |
| 212 | 349 | ||
| 213 | std::memcpy(output.data(), ¶ms, output.size()); | 350 | u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)}; |
| 214 | return NvResult::Success; | 351 | VAddr cpu_address{mapping->ptr + params.buffer_offset}; |
| 215 | } else { | 352 | |
| 216 | LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); | 353 | gmmu->Map(gpu_address, cpu_address, params.mapping_size, mapping->big_page); |
| 217 | 354 | ||
| 218 | std::memcpy(output.data(), ¶ms, output.size()); | 355 | return NvResult::Success; |
| 219 | return NvResult::InvalidState; | 356 | } catch (const std::out_of_range&) { |
| 357 | LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: 0x{:X}", | ||
| 358 | params.offset); | ||
| 359 | return NvResult::BadValue; | ||
| 220 | } | 360 | } |
| 221 | } | 361 | } |
| 222 | 362 | ||
| 223 | // We can only map objects that have already been assigned a CPU address. | 363 | auto handle{nvmap.GetHandle(params.handle)}; |
| 224 | ASSERT(object->status == nvmap::Object::Status::Allocated); | 364 | if (!handle) { |
| 225 | 365 | return NvResult::BadValue; | |
| 226 | const auto physical_address{object->addr + params.buffer_offset}; | ||
| 227 | u64 size{params.mapping_size}; | ||
| 228 | if (!size) { | ||
| 229 | size = object->size; | ||
| 230 | } | 366 | } |
| 231 | 367 | ||
| 232 | const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None}; | 368 | VAddr cpu_address{static_cast<VAddr>(handle->address + params.buffer_offset)}; |
| 233 | if (is_alloc) { | 369 | u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; |
| 234 | params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size); | 370 | |
| 235 | } else { | 371 | bool big_page{[&]() { |
| 236 | params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); | 372 | if (Common::IsAligned(handle->align, vm.big_page_size)) { |
| 237 | } | 373 | return true; |
| 374 | } else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) { | ||
| 375 | return false; | ||
| 376 | } else { | ||
| 377 | ASSERT(false); | ||
| 378 | return false; | ||
| 379 | } | ||
| 380 | }()}; | ||
| 381 | |||
| 382 | if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) { | ||
| 383 | auto alloc{allocation_map.upper_bound(params.offset)}; | ||
| 238 | 384 | ||
| 239 | auto result = NvResult::Success; | 385 | if (alloc-- == allocation_map.begin() || |
| 240 | if (!params.offset) { | 386 | (params.offset - alloc->first) + size > alloc->second.size) { |
| 241 | LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); | 387 | ASSERT_MSG(false, "Cannot perform a fixed mapping into an unallocated region!"); |
| 242 | result = NvResult::InvalidState; | 388 | return NvResult::BadValue; |
| 389 | } | ||
| 390 | |||
| 391 | const bool use_big_pages = alloc->second.big_pages && big_page; | ||
| 392 | gmmu->Map(params.offset, cpu_address, size, use_big_pages); | ||
| 393 | |||
| 394 | auto mapping{std::make_shared<Mapping>(cpu_address, params.offset, size, true, | ||
| 395 | use_big_pages, alloc->second.sparse)}; | ||
| 396 | alloc->second.mappings.push_back(mapping); | ||
| 397 | mapping_map[params.offset] = mapping; | ||
| 243 | } else { | 398 | } else { |
| 244 | AddBufferMap(params.offset, size, physical_address, is_alloc); | 399 | |
| 400 | auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; | ||
| 401 | u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; | ||
| 402 | u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; | ||
| 403 | |||
| 404 | params.offset = static_cast<u64>(allocator.Allocate( | ||
| 405 | static_cast<u32>(Common::AlignUp(size, page_size) >> page_size_bits))) | ||
| 406 | << page_size_bits; | ||
| 407 | if (!params.offset) { | ||
| 408 | ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); | ||
| 409 | return NvResult::InsufficientMemory; | ||
| 410 | } | ||
| 411 | |||
| 412 | gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size), big_page); | ||
| 413 | |||
| 414 | auto mapping{ | ||
| 415 | std::make_shared<Mapping>(cpu_address, params.offset, size, false, big_page, false)}; | ||
| 416 | mapping_map[params.offset] = mapping; | ||
| 245 | } | 417 | } |
| 246 | 418 | ||
| 247 | std::memcpy(output.data(), ¶ms, output.size()); | 419 | std::memcpy(output.data(), ¶ms, output.size()); |
| 248 | return result; | 420 | return NvResult::Success; |
| 249 | } | 421 | } |
| 250 | 422 | ||
| 251 | NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | 423 | NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { |
| @@ -254,47 +426,82 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8 | |||
| 254 | 426 | ||
| 255 | LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); | 427 | LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); |
| 256 | 428 | ||
| 257 | if (const auto size{RemoveBufferMap(params.offset)}; size) { | 429 | std::scoped_lock lock(mutex); |
| 258 | system.GPU().MemoryManager().Unmap(params.offset, *size); | 430 | |
| 259 | } else { | 431 | if (!vm.initialised) { |
| 260 | LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset); | 432 | return NvResult::BadValue; |
| 433 | } | ||
| 434 | |||
| 435 | try { | ||
| 436 | auto mapping{mapping_map.at(params.offset)}; | ||
| 437 | |||
| 438 | if (!mapping->fixed) { | ||
| 439 | auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; | ||
| 440 | u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; | ||
| 441 | |||
| 442 | allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits), | ||
| 443 | static_cast<u32>(mapping->size >> page_size_bits)); | ||
| 444 | } | ||
| 445 | |||
| 446 | // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state | ||
| 447 | // Only FreeSpace can unmap them fully | ||
| 448 | if (mapping->sparse_alloc) { | ||
| 449 | gmmu->MapSparse(params.offset, mapping->size, mapping->big_page); | ||
| 450 | } else { | ||
| 451 | gmmu->Unmap(params.offset, mapping->size); | ||
| 452 | } | ||
| 453 | |||
| 454 | mapping_map.erase(params.offset); | ||
| 455 | } catch (const std::out_of_range&) { | ||
| 456 | LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset); | ||
| 261 | } | 457 | } |
| 262 | 458 | ||
| 263 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 264 | return NvResult::Success; | 459 | return NvResult::Success; |
| 265 | } | 460 | } |
| 266 | 461 | ||
| 267 | NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { | 462 | NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { |
| 268 | IoctlBindChannel params{}; | 463 | IoctlBindChannel params{}; |
| 269 | std::memcpy(¶ms, input.data(), input.size()); | 464 | std::memcpy(¶ms, input.data(), input.size()); |
| 270 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd); | 465 | LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); |
| 271 | 466 | ||
| 272 | channel = params.fd; | 467 | auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd); |
| 468 | gpu_channel_device->channel_state->memory_manager = gmmu; | ||
| 273 | return NvResult::Success; | 469 | return NvResult::Success; |
| 274 | } | 470 | } |
| 275 | 471 | ||
| 472 | void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) { | ||
| 473 | params.buf_size = 2 * sizeof(VaRegion); | ||
| 474 | |||
| 475 | params.regions = std::array<VaRegion, 2>{ | ||
| 476 | VaRegion{ | ||
| 477 | .offset = vm.small_page_allocator->GetVAStart() << VM::PAGE_SIZE_BITS, | ||
| 478 | .page_size = VM::YUZU_PAGESIZE, | ||
| 479 | ._pad0_{}, | ||
| 480 | .pages = vm.small_page_allocator->GetVALimit() - vm.small_page_allocator->GetVAStart(), | ||
| 481 | }, | ||
| 482 | VaRegion{ | ||
| 483 | .offset = vm.big_page_allocator->GetVAStart() << vm.big_page_size_bits, | ||
| 484 | .page_size = vm.big_page_size, | ||
| 485 | ._pad0_{}, | ||
| 486 | .pages = vm.big_page_allocator->GetVALimit() - vm.big_page_allocator->GetVAStart(), | ||
| 487 | }, | ||
| 488 | }; | ||
| 489 | } | ||
| 490 | |||
| 276 | NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { | 491 | NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { |
| 277 | IoctlGetVaRegions params{}; | 492 | IoctlGetVaRegions params{}; |
| 278 | std::memcpy(¶ms, input.data(), input.size()); | 493 | std::memcpy(¶ms, input.data(), input.size()); |
| 279 | 494 | ||
| 280 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, | 495 | LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, |
| 281 | params.buf_size); | 496 | params.buf_size); |
| 282 | |||
| 283 | params.buf_size = 0x30; | ||
| 284 | 497 | ||
| 285 | params.small = IoctlVaRegion{ | 498 | std::scoped_lock lock(mutex); |
| 286 | .offset = 0x04000000, | ||
| 287 | .page_size = DEFAULT_SMALL_PAGE_SIZE, | ||
| 288 | .pages = 0x3fbfff, | ||
| 289 | }; | ||
| 290 | 499 | ||
| 291 | params.big = IoctlVaRegion{ | 500 | if (!vm.initialised) { |
| 292 | .offset = 0x04000000, | 501 | return NvResult::BadValue; |
| 293 | .page_size = big_page_size, | 502 | } |
| 294 | .pages = 0x1bffff, | ||
| 295 | }; | ||
| 296 | 503 | ||
| 297 | // TODO(ogniK): This probably can stay stubbed but should add support way way later | 504 | GetVARegionsImpl(params); |
| 298 | 505 | ||
| 299 | std::memcpy(output.data(), ¶ms, output.size()); | 506 | std::memcpy(output.data(), ¶ms, output.size()); |
| 300 | return NvResult::Success; | 507 | return NvResult::Success; |
| @@ -305,62 +512,27 @@ NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u | |||
| 305 | IoctlGetVaRegions params{}; | 512 | IoctlGetVaRegions params{}; |
| 306 | std::memcpy(¶ms, input.data(), input.size()); | 513 | std::memcpy(¶ms, input.data(), input.size()); |
| 307 | 514 | ||
| 308 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, | 515 | LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, |
| 309 | params.buf_size); | 516 | params.buf_size); |
| 310 | |||
| 311 | params.buf_size = 0x30; | ||
| 312 | 517 | ||
| 313 | params.small = IoctlVaRegion{ | 518 | std::scoped_lock lock(mutex); |
| 314 | .offset = 0x04000000, | ||
| 315 | .page_size = 0x1000, | ||
| 316 | .pages = 0x3fbfff, | ||
| 317 | }; | ||
| 318 | 519 | ||
| 319 | params.big = IoctlVaRegion{ | 520 | if (!vm.initialised) { |
| 320 | .offset = 0x04000000, | 521 | return NvResult::BadValue; |
| 321 | .page_size = big_page_size, | 522 | } |
| 322 | .pages = 0x1bffff, | ||
| 323 | }; | ||
| 324 | 523 | ||
| 325 | // TODO(ogniK): This probably can stay stubbed but should add support way way later | 524 | GetVARegionsImpl(params); |
| 326 | 525 | ||
| 327 | std::memcpy(output.data(), ¶ms, output.size()); | 526 | std::memcpy(output.data(), ¶ms, output.size()); |
| 328 | std::memcpy(inline_output.data(), ¶ms.small, sizeof(IoctlVaRegion)); | 527 | std::memcpy(inline_output.data(), ¶ms.regions[0], sizeof(VaRegion)); |
| 329 | std::memcpy(inline_output.data() + sizeof(IoctlVaRegion), ¶ms.big, sizeof(IoctlVaRegion)); | 528 | std::memcpy(inline_output.data() + sizeof(VaRegion), ¶ms.regions[1], sizeof(VaRegion)); |
| 330 | 529 | ||
| 331 | return NvResult::Success; | 530 | return NvResult::Success; |
| 332 | } | 531 | } |
| 333 | 532 | ||
| 334 | std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { | 533 | Kernel::KEvent* nvhost_as_gpu::QueryEvent(u32 event_id) { |
| 335 | const auto end{buffer_mappings.upper_bound(gpu_addr)}; | 534 | LOG_CRITICAL(Service_NVDRV, "Unknown AS GPU Event {}", event_id); |
| 336 | for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) { | 535 | return nullptr; |
| 337 | if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) { | ||
| 338 | return iter->second; | ||
| 339 | } | ||
| 340 | } | ||
| 341 | |||
| 342 | return std::nullopt; | ||
| 343 | } | ||
| 344 | |||
| 345 | void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, | ||
| 346 | bool is_allocated) { | ||
| 347 | buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated}; | ||
| 348 | } | ||
| 349 | |||
| 350 | std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) { | ||
| 351 | if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) { | ||
| 352 | std::size_t size{}; | ||
| 353 | |||
| 354 | if (iter->second.IsAllocated()) { | ||
| 355 | size = iter->second.Size(); | ||
| 356 | } | ||
| 357 | |||
| 358 | buffer_mappings.erase(iter); | ||
| 359 | |||
| 360 | return size; | ||
| 361 | } | ||
| 362 | |||
| 363 | return std::nullopt; | ||
| 364 | } | 536 | } |
| 365 | 537 | ||
| 366 | } // namespace Service::Nvidia::Devices | 538 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 555843a6f..86fe71c75 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h | |||
| @@ -1,35 +1,50 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors |
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | 4 | ||
| 4 | #pragma once | 5 | #pragma once |
| 5 | 6 | ||
| 7 | #include <bit> | ||
| 8 | #include <list> | ||
| 6 | #include <map> | 9 | #include <map> |
| 7 | #include <memory> | 10 | #include <memory> |
| 11 | #include <mutex> | ||
| 8 | #include <optional> | 12 | #include <optional> |
| 9 | #include <vector> | 13 | #include <vector> |
| 10 | 14 | ||
| 15 | #include "common/address_space.h" | ||
| 11 | #include "common/common_funcs.h" | 16 | #include "common/common_funcs.h" |
| 12 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 13 | #include "common/swap.h" | 18 | #include "common/swap.h" |
| 19 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 14 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 20 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| 15 | 21 | ||
| 16 | namespace Service::Nvidia::Devices { | 22 | namespace Tegra { |
| 23 | class MemoryManager; | ||
| 24 | } // namespace Tegra | ||
| 25 | |||
| 26 | namespace Service::Nvidia { | ||
| 27 | class Module; | ||
| 28 | } | ||
| 17 | 29 | ||
| 18 | constexpr u32 DEFAULT_BIG_PAGE_SIZE = 1 << 16; | 30 | namespace Service::Nvidia::NvCore { |
| 19 | constexpr u32 DEFAULT_SMALL_PAGE_SIZE = 1 << 12; | 31 | class Container; |
| 32 | class NvMap; | ||
| 33 | } // namespace Service::Nvidia::NvCore | ||
| 20 | 34 | ||
| 21 | class nvmap; | 35 | namespace Service::Nvidia::Devices { |
| 22 | 36 | ||
| 23 | enum class AddressSpaceFlags : u32 { | 37 | enum class MappingFlags : u32 { |
| 24 | None = 0x0, | 38 | None = 0, |
| 25 | FixedOffset = 0x1, | 39 | Fixed = 1 << 0, |
| 26 | Remap = 0x100, | 40 | Sparse = 1 << 1, |
| 41 | Remap = 1 << 8, | ||
| 27 | }; | 42 | }; |
| 28 | DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); | 43 | DECLARE_ENUM_FLAG_OPERATORS(MappingFlags); |
| 29 | 44 | ||
| 30 | class nvhost_as_gpu final : public nvdevice { | 45 | class nvhost_as_gpu final : public nvdevice { |
| 31 | public: | 46 | public: |
| 32 | explicit nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_); | 47 | explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core); |
| 33 | ~nvhost_as_gpu() override; | 48 | ~nvhost_as_gpu() override; |
| 34 | 49 | ||
| 35 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 50 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -42,46 +57,17 @@ public: | |||
| 42 | void OnOpen(DeviceFD fd) override; | 57 | void OnOpen(DeviceFD fd) override; |
| 43 | void OnClose(DeviceFD fd) override; | 58 | void OnClose(DeviceFD fd) override; |
| 44 | 59 | ||
| 45 | private: | 60 | Kernel::KEvent* QueryEvent(u32 event_id) override; |
| 46 | class BufferMap final { | 61 | |
| 47 | public: | 62 | struct VaRegion { |
| 48 | constexpr BufferMap() = default; | 63 | u64 offset; |
| 49 | 64 | u32 page_size; | |
| 50 | constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_) | 65 | u32 _pad0_; |
| 51 | : start_addr{start_addr_}, end_addr{start_addr_ + size_} {} | 66 | u64 pages; |
| 52 | |||
| 53 | constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_, | ||
| 54 | bool is_allocated_) | ||
| 55 | : start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_}, | ||
| 56 | is_allocated{is_allocated_} {} | ||
| 57 | |||
| 58 | constexpr VAddr StartAddr() const { | ||
| 59 | return start_addr; | ||
| 60 | } | ||
| 61 | |||
| 62 | constexpr VAddr EndAddr() const { | ||
| 63 | return end_addr; | ||
| 64 | } | ||
| 65 | |||
| 66 | constexpr std::size_t Size() const { | ||
| 67 | return end_addr - start_addr; | ||
| 68 | } | ||
| 69 | |||
| 70 | constexpr VAddr CpuAddr() const { | ||
| 71 | return cpu_addr; | ||
| 72 | } | ||
| 73 | |||
| 74 | constexpr bool IsAllocated() const { | ||
| 75 | return is_allocated; | ||
| 76 | } | ||
| 77 | |||
| 78 | private: | ||
| 79 | GPUVAddr start_addr{}; | ||
| 80 | GPUVAddr end_addr{}; | ||
| 81 | VAddr cpu_addr{}; | ||
| 82 | bool is_allocated{}; | ||
| 83 | }; | 67 | }; |
| 68 | static_assert(sizeof(VaRegion) == 0x18); | ||
| 84 | 69 | ||
| 70 | private: | ||
| 85 | struct IoctlAllocAsEx { | 71 | struct IoctlAllocAsEx { |
| 86 | u32_le flags{}; // usually passes 1 | 72 | u32_le flags{}; // usually passes 1 |
| 87 | s32_le as_fd{}; // ignored; passes 0 | 73 | s32_le as_fd{}; // ignored; passes 0 |
| @@ -96,7 +82,7 @@ private: | |||
| 96 | struct IoctlAllocSpace { | 82 | struct IoctlAllocSpace { |
| 97 | u32_le pages{}; | 83 | u32_le pages{}; |
| 98 | u32_le page_size{}; | 84 | u32_le page_size{}; |
| 99 | AddressSpaceFlags flags{}; | 85 | MappingFlags flags{}; |
| 100 | INSERT_PADDING_WORDS(1); | 86 | INSERT_PADDING_WORDS(1); |
| 101 | union { | 87 | union { |
| 102 | u64_le offset; | 88 | u64_le offset; |
| @@ -113,19 +99,19 @@ private: | |||
| 113 | static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); | 99 | static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); |
| 114 | 100 | ||
| 115 | struct IoctlRemapEntry { | 101 | struct IoctlRemapEntry { |
| 116 | u16_le flags{}; | 102 | u16 flags; |
| 117 | u16_le kind{}; | 103 | u16 kind; |
| 118 | u32_le nvmap_handle{}; | 104 | NvCore::NvMap::Handle::Id handle; |
| 119 | u32_le map_offset{}; | 105 | u32 handle_offset_big_pages; |
| 120 | u32_le offset{}; | 106 | u32 as_offset_big_pages; |
| 121 | u32_le pages{}; | 107 | u32 big_pages; |
| 122 | }; | 108 | }; |
| 123 | static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); | 109 | static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); |
| 124 | 110 | ||
| 125 | struct IoctlMapBufferEx { | 111 | struct IoctlMapBufferEx { |
| 126 | AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable | 112 | MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable |
| 127 | u32_le kind{}; // -1 is default | 113 | u32_le kind{}; // -1 is default |
| 128 | u32_le nvmap_handle{}; | 114 | NvCore::NvMap::Handle::Id handle; |
| 129 | u32_le page_size{}; // 0 means don't care | 115 | u32_le page_size{}; // 0 means don't care |
| 130 | s64_le buffer_offset{}; | 116 | s64_le buffer_offset{}; |
| 131 | u64_le mapping_size{}; | 117 | u64_le mapping_size{}; |
| @@ -143,27 +129,15 @@ private: | |||
| 143 | }; | 129 | }; |
| 144 | static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); | 130 | static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); |
| 145 | 131 | ||
| 146 | struct IoctlVaRegion { | ||
| 147 | u64_le offset{}; | ||
| 148 | u32_le page_size{}; | ||
| 149 | INSERT_PADDING_WORDS(1); | ||
| 150 | u64_le pages{}; | ||
| 151 | }; | ||
| 152 | static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size"); | ||
| 153 | |||
| 154 | struct IoctlGetVaRegions { | 132 | struct IoctlGetVaRegions { |
| 155 | u64_le buf_addr{}; // (contained output user ptr on linux, ignored) | 133 | u64_le buf_addr{}; // (contained output user ptr on linux, ignored) |
| 156 | u32_le buf_size{}; // forced to 2*sizeof(struct va_region) | 134 | u32_le buf_size{}; // forced to 2*sizeof(struct va_region) |
| 157 | u32_le reserved{}; | 135 | u32_le reserved{}; |
| 158 | IoctlVaRegion small{}; | 136 | std::array<VaRegion, 2> regions{}; |
| 159 | IoctlVaRegion big{}; | ||
| 160 | }; | 137 | }; |
| 161 | static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, | 138 | static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, |
| 162 | "IoctlGetVaRegions is incorrect size"); | 139 | "IoctlGetVaRegions is incorrect size"); |
| 163 | 140 | ||
| 164 | s32 channel{}; | ||
| 165 | u32 big_page_size{DEFAULT_BIG_PAGE_SIZE}; | ||
| 166 | |||
| 167 | NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output); | 141 | NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output); |
| 168 | NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); | 142 | NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); |
| 169 | NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output); | 143 | NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output); |
| @@ -172,18 +146,75 @@ private: | |||
| 172 | NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output); | 146 | NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output); |
| 173 | NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output); | 147 | NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output); |
| 174 | 148 | ||
| 149 | void GetVARegionsImpl(IoctlGetVaRegions& params); | ||
| 175 | NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); | 150 | NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); |
| 176 | NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, | 151 | NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, |
| 177 | std::vector<u8>& inline_output); | 152 | std::vector<u8>& inline_output); |
| 178 | 153 | ||
| 179 | std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; | 154 | void FreeMappingLocked(u64 offset); |
| 180 | void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); | 155 | |
| 181 | std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr); | 156 | Module& module; |
| 157 | |||
| 158 | NvCore::Container& container; | ||
| 159 | NvCore::NvMap& nvmap; | ||
| 182 | 160 | ||
| 183 | std::shared_ptr<nvmap> nvmap_dev; | 161 | struct Mapping { |
| 162 | VAddr ptr; | ||
| 163 | u64 offset; | ||
| 164 | u64 size; | ||
| 165 | bool fixed; | ||
| 166 | bool big_page; // Only valid if fixed == false | ||
| 167 | bool sparse_alloc; | ||
| 168 | |||
| 169 | Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_) | ||
| 170 | : ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_), | ||
| 171 | sparse_alloc(sparse_alloc_) {} | ||
| 172 | }; | ||
| 173 | |||
| 174 | struct Allocation { | ||
| 175 | u64 size; | ||
| 176 | std::list<std::shared_ptr<Mapping>> mappings; | ||
| 177 | u32 page_size; | ||
| 178 | bool sparse; | ||
| 179 | bool big_pages; | ||
| 180 | }; | ||
| 184 | 181 | ||
| 185 | // This is expected to be ordered, therefore we must use a map, not unordered_map | 182 | std::map<u64, std::shared_ptr<Mapping>> |
| 186 | std::map<GPUVAddr, BufferMap> buffer_mappings; | 183 | mapping_map; //!< This maps the base addresses of mapped buffers to their total sizes and |
| 184 | //!< mapping type, this is needed as what was originally a single buffer may | ||
| 185 | //!< have been split into multiple GPU side buffers with the remap flag. | ||
| 186 | std::map<u64, Allocation> allocation_map; //!< Holds allocations created by AllocSpace from | ||
| 187 | //!< which fixed buffers can be mapped into | ||
| 188 | std::mutex mutex; //!< Locks all AS operations | ||
| 189 | |||
| 190 | struct VM { | ||
| 191 | static constexpr u32 YUZU_PAGESIZE{0x1000}; | ||
| 192 | static constexpr u32 PAGE_SIZE_BITS{std::countr_zero(YUZU_PAGESIZE)}; | ||
| 193 | |||
| 194 | static constexpr u32 SUPPORTED_BIG_PAGE_SIZES{0x30000}; | ||
| 195 | static constexpr u32 DEFAULT_BIG_PAGE_SIZE{0x20000}; | ||
| 196 | u32 big_page_size{DEFAULT_BIG_PAGE_SIZE}; | ||
| 197 | u32 big_page_size_bits{std::countr_zero(DEFAULT_BIG_PAGE_SIZE)}; | ||
| 198 | |||
| 199 | static constexpr u32 VA_START_SHIFT{10}; | ||
| 200 | static constexpr u64 DEFAULT_VA_SPLIT{1ULL << 34}; | ||
| 201 | static constexpr u64 DEFAULT_VA_RANGE{1ULL << 37}; | ||
| 202 | u64 va_range_start{DEFAULT_BIG_PAGE_SIZE << VA_START_SHIFT}; | ||
| 203 | u64 va_range_split{DEFAULT_VA_SPLIT}; | ||
| 204 | u64 va_range_end{DEFAULT_VA_RANGE}; | ||
| 205 | |||
| 206 | using Allocator = Common::FlatAllocator<u32, 0, 32>; | ||
| 207 | |||
| 208 | std::unique_ptr<Allocator> big_page_allocator; | ||
| 209 | std::shared_ptr<Allocator> | ||
| 210 | small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel | ||
| 211 | |||
| 212 | bool initialised{}; | ||
| 213 | } vm; | ||
| 214 | std::shared_ptr<Tegra::MemoryManager> gmmu; | ||
| 215 | |||
| 216 | // s32 channel{}; | ||
| 217 | // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; | ||
| 187 | }; | 218 | }; |
| 188 | 219 | ||
| 189 | } // namespace Service::Nvidia::Devices | 220 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 527531f29..5bee4a3d3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | |||
| @@ -1,24 +1,39 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors |
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | 4 | ||
| 5 | #include <bit> | ||
| 4 | #include <cstdlib> | 6 | #include <cstdlib> |
| 5 | #include <cstring> | 7 | #include <cstring> |
| 6 | 8 | ||
| 9 | #include <fmt/format.h> | ||
| 7 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/scope_exit.h" | ||
| 9 | #include "core/core.h" | 13 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/k_event.h" | 14 | #include "core/hle/kernel/k_event.h" |
| 11 | #include "core/hle/kernel/k_writable_event.h" | 15 | #include "core/hle/kernel/k_writable_event.h" |
| 16 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 17 | #include "core/hle/service/nvdrv/core/syncpoint_manager.h" | ||
| 12 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" | 18 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" |
| 13 | #include "video_core/gpu.h" | 19 | #include "video_core/gpu.h" |
| 20 | #include "video_core/host1x/host1x.h" | ||
| 14 | 21 | ||
| 15 | namespace Service::Nvidia::Devices { | 22 | namespace Service::Nvidia::Devices { |
| 16 | 23 | ||
| 17 | nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, | 24 | nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, |
| 18 | SyncpointManager& syncpoint_manager_) | 25 | NvCore::Container& core_) |
| 19 | : nvdevice{system_}, events_interface{events_interface_}, syncpoint_manager{ | 26 | : nvdevice{system_}, events_interface{events_interface_}, core{core_}, |
| 20 | syncpoint_manager_} {} | 27 | syncpoint_manager{core_.GetSyncpointManager()} {} |
| 21 | nvhost_ctrl::~nvhost_ctrl() = default; | 28 | |
| 29 | nvhost_ctrl::~nvhost_ctrl() { | ||
| 30 | for (auto& event : events) { | ||
| 31 | if (!event.registered) { | ||
| 32 | continue; | ||
| 33 | } | ||
| 34 | events_interface.FreeEvent(event.kevent); | ||
| 35 | } | ||
| 36 | } | ||
| 22 | 37 | ||
| 23 | NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 38 | NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| 24 | std::vector<u8>& output) { | 39 | std::vector<u8>& output) { |
| @@ -30,13 +45,15 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& | |||
| 30 | case 0x1c: | 45 | case 0x1c: |
| 31 | return IocCtrlClearEventWait(input, output); | 46 | return IocCtrlClearEventWait(input, output); |
| 32 | case 0x1d: | 47 | case 0x1d: |
| 33 | return IocCtrlEventWait(input, output, false); | ||
| 34 | case 0x1e: | ||
| 35 | return IocCtrlEventWait(input, output, true); | 48 | return IocCtrlEventWait(input, output, true); |
| 49 | case 0x1e: | ||
| 50 | return IocCtrlEventWait(input, output, false); | ||
| 36 | case 0x1f: | 51 | case 0x1f: |
| 37 | return IocCtrlEventRegister(input, output); | 52 | return IocCtrlEventRegister(input, output); |
| 38 | case 0x20: | 53 | case 0x20: |
| 39 | return IocCtrlEventUnregister(input, output); | 54 | return IocCtrlEventUnregister(input, output); |
| 55 | case 0x21: | ||
| 56 | return IocCtrlEventUnregisterBatch(input, output); | ||
| 40 | } | 57 | } |
| 41 | break; | 58 | break; |
| 42 | default: | 59 | default: |
| @@ -60,6 +77,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& | |||
| 60 | } | 77 | } |
| 61 | 78 | ||
| 62 | void nvhost_ctrl::OnOpen(DeviceFD fd) {} | 79 | void nvhost_ctrl::OnOpen(DeviceFD fd) {} |
| 80 | |||
| 63 | void nvhost_ctrl::OnClose(DeviceFD fd) {} | 81 | void nvhost_ctrl::OnClose(DeviceFD fd) {} |
| 64 | 82 | ||
| 65 | NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { | 83 | NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { |
| @@ -71,116 +89,167 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector | |||
| 71 | } | 89 | } |
| 72 | 90 | ||
| 73 | NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, | 91 | NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, |
| 74 | bool is_async) { | 92 | bool is_allocation) { |
| 75 | IocCtrlEventWaitParams params{}; | 93 | IocCtrlEventWaitParams params{}; |
| 76 | std::memcpy(¶ms, input.data(), sizeof(params)); | 94 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 77 | LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", | 95 | LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}", |
| 78 | params.syncpt_id, params.threshold, params.timeout, is_async); | 96 | params.fence.id, params.fence.value, params.timeout, is_allocation); |
| 79 | 97 | ||
| 80 | if (params.syncpt_id >= MaxSyncPoints) { | 98 | bool must_unmark_fail = !is_allocation; |
| 81 | return NvResult::BadParameter; | 99 | const u32 event_id = params.value.raw; |
| 82 | } | 100 | SCOPE_EXIT({ |
| 101 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 102 | if (must_unmark_fail) { | ||
| 103 | events[event_id].fails = 0; | ||
| 104 | } | ||
| 105 | }); | ||
| 83 | 106 | ||
| 84 | u32 event_id = params.value & 0x00FF; | 107 | const u32 fence_id = static_cast<u32>(params.fence.id); |
| 85 | 108 | ||
| 86 | if (event_id >= MaxNvEvents) { | 109 | if (fence_id >= MaxSyncPoints) { |
| 87 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 88 | return NvResult::BadParameter; | 110 | return NvResult::BadParameter; |
| 89 | } | 111 | } |
| 90 | 112 | ||
| 91 | if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { | 113 | if (params.fence.value == 0) { |
| 92 | params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id); | 114 | if (!syncpoint_manager.IsSyncpointAllocated(params.fence.id)) { |
| 93 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 115 | LOG_WARNING(Service_NVDRV, |
| 94 | events_interface.failed[event_id] = false; | 116 | "Unallocated syncpt_id={}, threshold={}, timeout={}, is_allocation={}", |
| 117 | params.fence.id, params.fence.value, params.timeout, is_allocation); | ||
| 118 | } else { | ||
| 119 | params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id); | ||
| 120 | } | ||
| 95 | return NvResult::Success; | 121 | return NvResult::Success; |
| 96 | } | 122 | } |
| 97 | 123 | ||
| 98 | if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id); | 124 | if (syncpoint_manager.IsFenceSignalled(params.fence)) { |
| 99 | syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { | 125 | params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id); |
| 100 | params.value = new_value; | ||
| 101 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 102 | events_interface.failed[event_id] = false; | ||
| 103 | return NvResult::Success; | 126 | return NvResult::Success; |
| 104 | } | 127 | } |
| 105 | 128 | ||
| 106 | auto& event = events_interface.events[event_id]; | 129 | if (const auto new_value = syncpoint_manager.UpdateMin(fence_id); |
| 107 | auto& gpu = system.GPU(); | 130 | syncpoint_manager.IsFenceSignalled(params.fence)) { |
| 108 | 131 | params.value.raw = new_value; | |
| 109 | // This is mostly to take into account unimplemented features. As synced | ||
| 110 | // gpu is always synced. | ||
| 111 | if (!gpu.IsAsync()) { | ||
| 112 | event.event->GetWritableEvent().Signal(); | ||
| 113 | return NvResult::Success; | ||
| 114 | } | ||
| 115 | const u32 current_syncpoint_value = event.fence.value; | ||
| 116 | const s32 diff = current_syncpoint_value - params.threshold; | ||
| 117 | if (diff >= 0) { | ||
| 118 | event.event->GetWritableEvent().Signal(); | ||
| 119 | params.value = current_syncpoint_value; | ||
| 120 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 121 | events_interface.failed[event_id] = false; | ||
| 122 | return NvResult::Success; | 132 | return NvResult::Success; |
| 123 | } | 133 | } |
| 124 | const u32 target_value = current_syncpoint_value - diff; | ||
| 125 | 134 | ||
| 126 | if (!is_async) { | 135 | auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager(); |
| 127 | params.value = 0; | 136 | const u32 target_value = params.fence.value; |
| 137 | |||
| 138 | auto lock = NvEventsLock(); | ||
| 139 | |||
| 140 | u32 slot = [&]() { | ||
| 141 | if (is_allocation) { | ||
| 142 | params.value.raw = 0; | ||
| 143 | return FindFreeNvEvent(fence_id); | ||
| 144 | } else { | ||
| 145 | return params.value.raw; | ||
| 146 | } | ||
| 147 | }(); | ||
| 148 | |||
| 149 | must_unmark_fail = false; | ||
| 150 | |||
| 151 | const auto check_failing = [&]() { | ||
| 152 | if (events[slot].fails > 2) { | ||
| 153 | { | ||
| 154 | auto lk = system.StallProcesses(); | ||
| 155 | host1x_syncpoint_manager.WaitHost(fence_id, target_value); | ||
| 156 | system.UnstallProcesses(); | ||
| 157 | } | ||
| 158 | params.value.raw = target_value; | ||
| 159 | return true; | ||
| 160 | } | ||
| 161 | return false; | ||
| 162 | }; | ||
| 163 | |||
| 164 | if (slot >= MaxNvEvents) { | ||
| 165 | return NvResult::BadParameter; | ||
| 128 | } | 166 | } |
| 129 | 167 | ||
| 130 | if (params.timeout == 0) { | 168 | if (params.timeout == 0) { |
| 131 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 169 | if (check_failing()) { |
| 170 | events[slot].fails = 0; | ||
| 171 | return NvResult::Success; | ||
| 172 | } | ||
| 132 | return NvResult::Timeout; | 173 | return NvResult::Timeout; |
| 133 | } | 174 | } |
| 134 | 175 | ||
| 135 | EventState status = events_interface.status[event_id]; | 176 | auto& event = events[slot]; |
| 136 | const bool bad_parameter = status == EventState::Busy; | 177 | |
| 137 | if (bad_parameter) { | 178 | if (!event.registered) { |
| 138 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 139 | return NvResult::BadParameter; | 179 | return NvResult::BadParameter; |
| 140 | } | 180 | } |
| 141 | events_interface.SetEventStatus(event_id, EventState::Waiting); | 181 | |
| 142 | events_interface.assigned_syncpt[event_id] = params.syncpt_id; | 182 | if (event.IsBeingUsed()) { |
| 143 | events_interface.assigned_value[event_id] = target_value; | 183 | return NvResult::BadParameter; |
| 144 | if (is_async) { | 184 | } |
| 145 | params.value = params.syncpt_id << 4; | 185 | |
| 146 | } else { | 186 | if (check_failing()) { |
| 147 | params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; | 187 | event.fails = 0; |
| 148 | } | ||
| 149 | params.value |= event_id; | ||
| 150 | event.event->GetWritableEvent().Clear(); | ||
| 151 | if (events_interface.failed[event_id]) { | ||
| 152 | { | ||
| 153 | auto lk = system.StallProcesses(); | ||
| 154 | gpu.WaitFence(params.syncpt_id, target_value); | ||
| 155 | system.UnstallProcesses(); | ||
| 156 | } | ||
| 157 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 158 | events_interface.failed[event_id] = false; | ||
| 159 | return NvResult::Success; | 188 | return NvResult::Success; |
| 160 | } | 189 | } |
| 161 | gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); | 190 | |
| 162 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 191 | params.value.raw = 0; |
| 192 | |||
| 193 | event.status.store(EventState::Waiting, std::memory_order_release); | ||
| 194 | event.assigned_syncpt = fence_id; | ||
| 195 | event.assigned_value = target_value; | ||
| 196 | if (is_allocation) { | ||
| 197 | params.value.syncpoint_id_for_allocation.Assign(static_cast<u16>(fence_id)); | ||
| 198 | params.value.event_allocated.Assign(1); | ||
| 199 | } else { | ||
| 200 | params.value.syncpoint_id.Assign(fence_id); | ||
| 201 | } | ||
| 202 | params.value.raw |= slot; | ||
| 203 | |||
| 204 | event.wait_handle = | ||
| 205 | host1x_syncpoint_manager.RegisterHostAction(fence_id, target_value, [this, slot]() { | ||
| 206 | auto& event_ = events[slot]; | ||
| 207 | if (event_.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == | ||
| 208 | EventState::Waiting) { | ||
| 209 | event_.kevent->GetWritableEvent().Signal(); | ||
| 210 | } | ||
| 211 | event_.status.store(EventState::Signalled, std::memory_order_release); | ||
| 212 | }); | ||
| 163 | return NvResult::Timeout; | 213 | return NvResult::Timeout; |
| 164 | } | 214 | } |
| 165 | 215 | ||
| 216 | NvResult nvhost_ctrl::FreeEvent(u32 slot) { | ||
| 217 | if (slot >= MaxNvEvents) { | ||
| 218 | return NvResult::BadParameter; | ||
| 219 | } | ||
| 220 | |||
| 221 | auto& event = events[slot]; | ||
| 222 | |||
| 223 | if (!event.registered) { | ||
| 224 | return NvResult::Success; | ||
| 225 | } | ||
| 226 | |||
| 227 | if (event.IsBeingUsed()) { | ||
| 228 | return NvResult::Busy; | ||
| 229 | } | ||
| 230 | |||
| 231 | FreeNvEvent(slot); | ||
| 232 | return NvResult::Success; | ||
| 233 | } | ||
| 234 | |||
| 166 | NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { | 235 | NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { |
| 167 | IocCtrlEventRegisterParams params{}; | 236 | IocCtrlEventRegisterParams params{}; |
| 168 | std::memcpy(¶ms, input.data(), sizeof(params)); | 237 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 169 | const u32 event_id = params.user_event_id & 0x00FF; | 238 | const u32 event_id = params.user_event_id; |
| 170 | LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); | 239 | LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); |
| 171 | if (event_id >= MaxNvEvents) { | 240 | if (event_id >= MaxNvEvents) { |
| 172 | return NvResult::BadParameter; | 241 | return NvResult::BadParameter; |
| 173 | } | 242 | } |
| 174 | if (events_interface.registered[event_id]) { | 243 | |
| 175 | const auto event_state = events_interface.status[event_id]; | 244 | auto lock = NvEventsLock(); |
| 176 | if (event_state != EventState::Free) { | 245 | |
| 177 | LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event"); | 246 | if (events[event_id].registered) { |
| 178 | events_interface.UnregisterEvent(event_id); | 247 | const auto result = FreeEvent(event_id); |
| 179 | } else { | 248 | if (result != NvResult::Success) { |
| 180 | return NvResult::BadParameter; | 249 | return result; |
| 181 | } | 250 | } |
| 182 | } | 251 | } |
| 183 | events_interface.RegisterEvent(event_id); | 252 | CreateNvEvent(event_id); |
| 184 | return NvResult::Success; | 253 | return NvResult::Success; |
| 185 | } | 254 | } |
| 186 | 255 | ||
| @@ -190,34 +259,142 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, | |||
| 190 | std::memcpy(¶ms, input.data(), sizeof(params)); | 259 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 191 | const u32 event_id = params.user_event_id & 0x00FF; | 260 | const u32 event_id = params.user_event_id & 0x00FF; |
| 192 | LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); | 261 | LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); |
| 193 | if (event_id >= MaxNvEvents) { | 262 | |
| 194 | return NvResult::BadParameter; | 263 | auto lock = NvEventsLock(); |
| 195 | } | 264 | return FreeEvent(event_id); |
| 196 | if (!events_interface.registered[event_id]) { | 265 | } |
| 197 | return NvResult::BadParameter; | 266 | |
| 267 | NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input, | ||
| 268 | std::vector<u8>& output) { | ||
| 269 | IocCtrlEventUnregisterBatchParams params{}; | ||
| 270 | std::memcpy(¶ms, input.data(), sizeof(params)); | ||
| 271 | u64 event_mask = params.user_events; | ||
| 272 | LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask); | ||
| 273 | |||
| 274 | auto lock = NvEventsLock(); | ||
| 275 | while (event_mask != 0) { | ||
| 276 | const u64 event_id = std::countr_zero(event_mask); | ||
| 277 | event_mask &= ~(1ULL << event_id); | ||
| 278 | const auto result = FreeEvent(static_cast<u32>(event_id)); | ||
| 279 | if (result != NvResult::Success) { | ||
| 280 | return result; | ||
| 281 | } | ||
| 198 | } | 282 | } |
| 199 | events_interface.UnregisterEvent(event_id); | ||
| 200 | return NvResult::Success; | 283 | return NvResult::Success; |
| 201 | } | 284 | } |
| 202 | 285 | ||
| 203 | NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { | 286 | NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { |
| 204 | IocCtrlEventSignalParams params{}; | 287 | IocCtrlEventClearParams params{}; |
| 205 | std::memcpy(¶ms, input.data(), sizeof(params)); | 288 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 206 | 289 | ||
| 207 | u32 event_id = params.event_id & 0x00FF; | 290 | u32 event_id = params.event_id.slot; |
| 208 | LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id); | 291 | LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id); |
| 209 | 292 | ||
| 210 | if (event_id >= MaxNvEvents) { | 293 | if (event_id >= MaxNvEvents) { |
| 211 | return NvResult::BadParameter; | 294 | return NvResult::BadParameter; |
| 212 | } | 295 | } |
| 213 | if (events_interface.status[event_id] == EventState::Waiting) { | ||
| 214 | events_interface.LiberateEvent(event_id); | ||
| 215 | } | ||
| 216 | events_interface.failed[event_id] = true; | ||
| 217 | 296 | ||
| 218 | syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id); | 297 | auto lock = NvEventsLock(); |
| 298 | |||
| 299 | auto& event = events[event_id]; | ||
| 300 | if (event.status.exchange(EventState::Cancelling, std::memory_order_acq_rel) == | ||
| 301 | EventState::Waiting) { | ||
| 302 | auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager(); | ||
| 303 | host1x_syncpoint_manager.DeregisterHostAction(event.assigned_syncpt, event.wait_handle); | ||
| 304 | syncpoint_manager.UpdateMin(event.assigned_syncpt); | ||
| 305 | event.wait_handle = {}; | ||
| 306 | } | ||
| 307 | event.fails++; | ||
| 308 | event.status.store(EventState::Cancelled, std::memory_order_release); | ||
| 309 | event.kevent->GetWritableEvent().Clear(); | ||
| 219 | 310 | ||
| 220 | return NvResult::Success; | 311 | return NvResult::Success; |
| 221 | } | 312 | } |
| 222 | 313 | ||
| 314 | Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) { | ||
| 315 | const auto desired_event = SyncpointEventValue{.raw = event_id}; | ||
| 316 | |||
| 317 | const bool allocated = desired_event.event_allocated.Value() != 0; | ||
| 318 | const u32 slot{allocated ? desired_event.partial_slot.Value() | ||
| 319 | : static_cast<u32>(desired_event.slot)}; | ||
| 320 | if (slot >= MaxNvEvents) { | ||
| 321 | ASSERT(false); | ||
| 322 | return nullptr; | ||
| 323 | } | ||
| 324 | |||
| 325 | const u32 syncpoint_id{allocated ? desired_event.syncpoint_id_for_allocation.Value() | ||
| 326 | : desired_event.syncpoint_id.Value()}; | ||
| 327 | |||
| 328 | auto lock = NvEventsLock(); | ||
| 329 | |||
| 330 | auto& event = events[slot]; | ||
| 331 | if (event.registered && event.assigned_syncpt == syncpoint_id) { | ||
| 332 | ASSERT(event.kevent); | ||
| 333 | return event.kevent; | ||
| 334 | } | ||
| 335 | // Is this possible in hardware? | ||
| 336 | ASSERT_MSG(false, "Slot:{}, SyncpointID:{}, requested", slot, syncpoint_id); | ||
| 337 | return nullptr; | ||
| 338 | } | ||
| 339 | |||
| 340 | std::unique_lock<std::mutex> nvhost_ctrl::NvEventsLock() { | ||
| 341 | return std::unique_lock<std::mutex>(events_mutex); | ||
| 342 | } | ||
| 343 | |||
| 344 | void nvhost_ctrl::CreateNvEvent(u32 event_id) { | ||
| 345 | auto& event = events[event_id]; | ||
| 346 | ASSERT(!event.kevent); | ||
| 347 | ASSERT(!event.registered); | ||
| 348 | ASSERT(!event.IsBeingUsed()); | ||
| 349 | event.kevent = events_interface.CreateEvent(fmt::format("NVCTRL::NvEvent_{}", event_id)); | ||
| 350 | event.status = EventState::Available; | ||
| 351 | event.registered = true; | ||
| 352 | const u64 mask = 1ULL << event_id; | ||
| 353 | event.fails = 0; | ||
| 354 | events_mask |= mask; | ||
| 355 | event.assigned_syncpt = 0; | ||
| 356 | } | ||
| 357 | |||
| 358 | void nvhost_ctrl::FreeNvEvent(u32 event_id) { | ||
| 359 | auto& event = events[event_id]; | ||
| 360 | ASSERT(event.kevent); | ||
| 361 | ASSERT(event.registered); | ||
| 362 | ASSERT(!event.IsBeingUsed()); | ||
| 363 | events_interface.FreeEvent(event.kevent); | ||
| 364 | event.kevent = nullptr; | ||
| 365 | event.status = EventState::Available; | ||
| 366 | event.registered = false; | ||
| 367 | const u64 mask = ~(1ULL << event_id); | ||
| 368 | events_mask &= mask; | ||
| 369 | } | ||
| 370 | |||
| 371 | u32 nvhost_ctrl::FindFreeNvEvent(u32 syncpoint_id) { | ||
| 372 | u32 slot{MaxNvEvents}; | ||
| 373 | u32 free_slot{MaxNvEvents}; | ||
| 374 | for (u32 i = 0; i < MaxNvEvents; i++) { | ||
| 375 | auto& event = events[i]; | ||
| 376 | if (event.registered) { | ||
| 377 | if (!event.IsBeingUsed()) { | ||
| 378 | slot = i; | ||
| 379 | if (event.assigned_syncpt == syncpoint_id) { | ||
| 380 | return slot; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | } else if (free_slot == MaxNvEvents) { | ||
| 384 | free_slot = i; | ||
| 385 | } | ||
| 386 | } | ||
| 387 | if (free_slot < MaxNvEvents) { | ||
| 388 | CreateNvEvent(free_slot); | ||
| 389 | return free_slot; | ||
| 390 | } | ||
| 391 | |||
| 392 | if (slot < MaxNvEvents) { | ||
| 393 | return slot; | ||
| 394 | } | ||
| 395 | |||
| 396 | LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event"); | ||
| 397 | return 0; | ||
| 398 | } | ||
| 399 | |||
| 223 | } // namespace Service::Nvidia::Devices | 400 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 4fbb89b15..0b56d7070 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h | |||
| @@ -1,20 +1,28 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors |
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | 4 | ||
| 4 | #pragma once | 5 | #pragma once |
| 5 | 6 | ||
| 6 | #include <array> | 7 | #include <array> |
| 7 | #include <vector> | 8 | #include <vector> |
| 9 | #include "common/bit_field.h" | ||
| 8 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 9 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 11 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| 10 | #include "core/hle/service/nvdrv/nvdrv.h" | 12 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 13 | #include "video_core/host1x/syncpoint_manager.h" | ||
| 14 | |||
| 15 | namespace Service::Nvidia::NvCore { | ||
| 16 | class Container; | ||
| 17 | class SyncpointManager; | ||
| 18 | } // namespace Service::Nvidia::NvCore | ||
| 11 | 19 | ||
| 12 | namespace Service::Nvidia::Devices { | 20 | namespace Service::Nvidia::Devices { |
| 13 | 21 | ||
| 14 | class nvhost_ctrl final : public nvdevice { | 22 | class nvhost_ctrl final : public nvdevice { |
| 15 | public: | 23 | public: |
| 16 | explicit nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, | 24 | explicit nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, |
| 17 | SyncpointManager& syncpoint_manager_); | 25 | NvCore::Container& core); |
| 18 | ~nvhost_ctrl() override; | 26 | ~nvhost_ctrl() override; |
| 19 | 27 | ||
| 20 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 28 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -27,7 +35,70 @@ public: | |||
| 27 | void OnOpen(DeviceFD fd) override; | 35 | void OnOpen(DeviceFD fd) override; |
| 28 | void OnClose(DeviceFD fd) override; | 36 | void OnClose(DeviceFD fd) override; |
| 29 | 37 | ||
| 38 | Kernel::KEvent* QueryEvent(u32 event_id) override; | ||
| 39 | |||
| 40 | union SyncpointEventValue { | ||
| 41 | u32 raw; | ||
| 42 | |||
| 43 | union { | ||
| 44 | BitField<0, 4, u32> partial_slot; | ||
| 45 | BitField<4, 28, u32> syncpoint_id; | ||
| 46 | }; | ||
| 47 | |||
| 48 | struct { | ||
| 49 | u16 slot; | ||
| 50 | union { | ||
| 51 | BitField<0, 12, u16> syncpoint_id_for_allocation; | ||
| 52 | BitField<12, 1, u16> event_allocated; | ||
| 53 | }; | ||
| 54 | }; | ||
| 55 | }; | ||
| 56 | static_assert(sizeof(SyncpointEventValue) == sizeof(u32)); | ||
| 57 | |||
| 30 | private: | 58 | private: |
| 59 | struct InternalEvent { | ||
| 60 | // Mask representing registered events | ||
| 61 | |||
| 62 | // Each kernel event associated to an NV event | ||
| 63 | Kernel::KEvent* kevent{}; | ||
| 64 | // The status of the current NVEvent | ||
| 65 | std::atomic<EventState> status{}; | ||
| 66 | |||
| 67 | // Tells the NVEvent that it has failed. | ||
| 68 | u32 fails{}; | ||
| 69 | // When an NVEvent is waiting on GPU interrupt, this is the sync_point | ||
| 70 | // associated with it. | ||
| 71 | u32 assigned_syncpt{}; | ||
| 72 | // This is the value of the GPU interrupt for which the NVEvent is waiting | ||
| 73 | // for. | ||
| 74 | u32 assigned_value{}; | ||
| 75 | |||
| 76 | // Tells if an NVEvent is registered or not | ||
| 77 | bool registered{}; | ||
| 78 | |||
| 79 | // Used for waiting on a syncpoint & canceling it. | ||
| 80 | Tegra::Host1x::SyncpointManager::ActionHandle wait_handle{}; | ||
| 81 | |||
| 82 | bool IsBeingUsed() const { | ||
| 83 | const auto current_status = status.load(std::memory_order_acquire); | ||
| 84 | return current_status == EventState::Waiting || | ||
| 85 | current_status == EventState::Cancelling || | ||
| 86 | current_status == EventState::Signalling; | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | std::unique_lock<std::mutex> NvEventsLock(); | ||
| 91 | |||
| 92 | void CreateNvEvent(u32 event_id); | ||
| 93 | |||
| 94 | void FreeNvEvent(u32 event_id); | ||
| 95 | |||
| 96 | u32 FindFreeNvEvent(u32 syncpoint_id); | ||
| 97 | |||
| 98 | std::array<InternalEvent, MaxNvEvents> events{}; | ||
| 99 | std::mutex events_mutex; | ||
| 100 | u64 events_mask{}; | ||
| 101 | |||
| 31 | struct IocSyncptReadParams { | 102 | struct IocSyncptReadParams { |
| 32 | u32_le id{}; | 103 | u32_le id{}; |
| 33 | u32_le value{}; | 104 | u32_le value{}; |
| @@ -83,27 +154,18 @@ private: | |||
| 83 | }; | 154 | }; |
| 84 | static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); | 155 | static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); |
| 85 | 156 | ||
| 86 | struct IocCtrlEventSignalParams { | 157 | struct IocCtrlEventClearParams { |
| 87 | u32_le event_id{}; | 158 | SyncpointEventValue event_id{}; |
| 88 | }; | 159 | }; |
| 89 | static_assert(sizeof(IocCtrlEventSignalParams) == 4, | 160 | static_assert(sizeof(IocCtrlEventClearParams) == 4, |
| 90 | "IocCtrlEventSignalParams is incorrect size"); | 161 | "IocCtrlEventClearParams is incorrect size"); |
| 91 | 162 | ||
| 92 | struct IocCtrlEventWaitParams { | 163 | struct IocCtrlEventWaitParams { |
| 93 | u32_le syncpt_id{}; | 164 | NvFence fence{}; |
| 94 | u32_le threshold{}; | ||
| 95 | s32_le timeout{}; | ||
| 96 | u32_le value{}; | ||
| 97 | }; | ||
| 98 | static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size"); | ||
| 99 | |||
| 100 | struct IocCtrlEventWaitAsyncParams { | ||
| 101 | u32_le syncpt_id{}; | ||
| 102 | u32_le threshold{}; | ||
| 103 | u32_le timeout{}; | 165 | u32_le timeout{}; |
| 104 | u32_le value{}; | 166 | SyncpointEventValue value{}; |
| 105 | }; | 167 | }; |
| 106 | static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16, | 168 | static_assert(sizeof(IocCtrlEventWaitParams) == 16, |
| 107 | "IocCtrlEventWaitAsyncParams is incorrect size"); | 169 | "IocCtrlEventWaitAsyncParams is incorrect size"); |
| 108 | 170 | ||
| 109 | struct IocCtrlEventRegisterParams { | 171 | struct IocCtrlEventRegisterParams { |
| @@ -118,19 +180,25 @@ private: | |||
| 118 | static_assert(sizeof(IocCtrlEventUnregisterParams) == 4, | 180 | static_assert(sizeof(IocCtrlEventUnregisterParams) == 4, |
| 119 | "IocCtrlEventUnregisterParams is incorrect size"); | 181 | "IocCtrlEventUnregisterParams is incorrect size"); |
| 120 | 182 | ||
| 121 | struct IocCtrlEventKill { | 183 | struct IocCtrlEventUnregisterBatchParams { |
| 122 | u64_le user_events{}; | 184 | u64_le user_events{}; |
| 123 | }; | 185 | }; |
| 124 | static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); | 186 | static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, |
| 187 | "IocCtrlEventKill is incorrect size"); | ||
| 125 | 188 | ||
| 126 | NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); | 189 | NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); |
| 127 | NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); | 190 | NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, |
| 191 | bool is_allocation); | ||
| 128 | NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); | 192 | NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); |
| 129 | NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); | 193 | NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); |
| 194 | NvResult IocCtrlEventUnregisterBatch(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 130 | NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); | 195 | NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); |
| 131 | 196 | ||
| 197 | NvResult FreeEvent(u32 slot); | ||
| 198 | |||
| 132 | EventInterface& events_interface; | 199 | EventInterface& events_interface; |
| 133 | SyncpointManager& syncpoint_manager; | 200 | NvCore::Container& core; |
| 201 | NvCore::SyncpointManager& syncpoint_manager; | ||
| 134 | }; | 202 | }; |
| 135 | 203 | ||
| 136 | } // namespace Service::Nvidia::Devices | 204 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 2b3b7efea..ced57dfe6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp | |||
| @@ -7,11 +7,19 @@ | |||
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 9 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" | 9 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" |
| 10 | #include "core/hle/service/nvdrv/nvdrv.h" | ||
| 10 | 11 | ||
| 11 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| 12 | 13 | ||
| 13 | nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_) : nvdevice{system_} {} | 14 | nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_) |
| 14 | nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; | 15 | : nvdevice{system_}, events_interface{events_interface_} { |
| 16 | error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier"); | ||
| 17 | unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent"); | ||
| 18 | } | ||
| 19 | nvhost_ctrl_gpu::~nvhost_ctrl_gpu() { | ||
| 20 | events_interface.FreeEvent(error_notifier_event); | ||
| 21 | events_interface.FreeEvent(unknown_event); | ||
| 22 | } | ||
| 15 | 23 | ||
| 16 | NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 24 | NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| 17 | std::vector<u8>& output) { | 25 | std::vector<u8>& output) { |
| @@ -286,4 +294,17 @@ NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u | |||
| 286 | return NvResult::Success; | 294 | return NvResult::Success; |
| 287 | } | 295 | } |
| 288 | 296 | ||
| 297 | Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) { | ||
| 298 | switch (event_id) { | ||
| 299 | case 1: | ||
| 300 | return error_notifier_event; | ||
| 301 | case 2: | ||
| 302 | return unknown_event; | ||
| 303 | default: { | ||
| 304 | LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); | ||
| 305 | } | ||
| 306 | } | ||
| 307 | return nullptr; | ||
| 308 | } | ||
| 309 | |||
| 289 | } // namespace Service::Nvidia::Devices | 310 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 97e9a90cb..1e8f254e2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h | |||
| @@ -10,11 +10,15 @@ | |||
| 10 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 11 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 11 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| 12 | 12 | ||
| 13 | namespace Service::Nvidia { | ||
| 14 | class EventInterface; | ||
| 15 | } | ||
| 16 | |||
| 13 | namespace Service::Nvidia::Devices { | 17 | namespace Service::Nvidia::Devices { |
| 14 | 18 | ||
| 15 | class nvhost_ctrl_gpu final : public nvdevice { | 19 | class nvhost_ctrl_gpu final : public nvdevice { |
| 16 | public: | 20 | public: |
| 17 | explicit nvhost_ctrl_gpu(Core::System& system_); | 21 | explicit nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_); |
| 18 | ~nvhost_ctrl_gpu() override; | 22 | ~nvhost_ctrl_gpu() override; |
| 19 | 23 | ||
| 20 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 24 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -27,6 +31,8 @@ public: | |||
| 27 | void OnOpen(DeviceFD fd) override; | 31 | void OnOpen(DeviceFD fd) override; |
| 28 | void OnClose(DeviceFD fd) override; | 32 | void OnClose(DeviceFD fd) override; |
| 29 | 33 | ||
| 34 | Kernel::KEvent* QueryEvent(u32 event_id) override; | ||
| 35 | |||
| 30 | private: | 36 | private: |
| 31 | struct IoctlGpuCharacteristics { | 37 | struct IoctlGpuCharacteristics { |
| 32 | u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) | 38 | u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) |
| @@ -160,6 +166,12 @@ private: | |||
| 160 | NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); | 166 | NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); |
| 161 | NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output); | 167 | NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output); |
| 162 | NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); | 168 | NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); |
| 169 | |||
| 170 | EventInterface& events_interface; | ||
| 171 | |||
| 172 | // Events | ||
| 173 | Kernel::KEvent* error_notifier_event; | ||
| 174 | Kernel::KEvent* unknown_event; | ||
| 163 | }; | 175 | }; |
| 164 | 176 | ||
| 165 | } // namespace Service::Nvidia::Devices | 177 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index b98e63011..45a759fa8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | |||
| @@ -5,29 +5,46 @@ | |||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 9 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 10 | #include "core/hle/service/nvdrv/core/syncpoint_manager.h" | ||
| 8 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" | 11 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" |
| 9 | #include "core/hle/service/nvdrv/syncpoint_manager.h" | 12 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 10 | #include "core/memory.h" | 13 | #include "core/memory.h" |
| 14 | #include "video_core/control/channel_state.h" | ||
| 15 | #include "video_core/engines/puller.h" | ||
| 11 | #include "video_core/gpu.h" | 16 | #include "video_core/gpu.h" |
| 17 | #include "video_core/host1x/host1x.h" | ||
| 12 | 18 | ||
| 13 | namespace Service::Nvidia::Devices { | 19 | namespace Service::Nvidia::Devices { |
| 14 | namespace { | 20 | namespace { |
| 15 | Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoint_id) { | 21 | Tegra::CommandHeader BuildFenceAction(Tegra::Engines::Puller::FenceOperation op, u32 syncpoint_id) { |
| 16 | Tegra::GPU::FenceAction result{}; | 22 | Tegra::Engines::Puller::FenceAction result{}; |
| 17 | result.op.Assign(op); | 23 | result.op.Assign(op); |
| 18 | result.syncpoint_id.Assign(syncpoint_id); | 24 | result.syncpoint_id.Assign(syncpoint_id); |
| 19 | return {result.raw}; | 25 | return {result.raw}; |
| 20 | } | 26 | } |
| 21 | } // namespace | 27 | } // namespace |
| 22 | 28 | ||
| 23 | nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 29 | nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_, |
| 24 | SyncpointManager& syncpoint_manager_) | 30 | NvCore::Container& core_) |
| 25 | : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} { | 31 | : nvdevice{system_}, events_interface{events_interface_}, core{core_}, |
| 26 | channel_fence.id = syncpoint_manager_.AllocateSyncpoint(); | 32 | syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()}, |
| 27 | channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); | 33 | channel_state{system.GPU().AllocateChannel()} { |
| 34 | channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); | ||
| 35 | sm_exception_breakpoint_int_report_event = | ||
| 36 | events_interface.CreateEvent("GpuChannelSMExceptionBreakpointInt"); | ||
| 37 | sm_exception_breakpoint_pause_report_event = | ||
| 38 | events_interface.CreateEvent("GpuChannelSMExceptionBreakpointPause"); | ||
| 39 | error_notifier_event = events_interface.CreateEvent("GpuChannelErrorNotifier"); | ||
| 28 | } | 40 | } |
| 29 | 41 | ||
| 30 | nvhost_gpu::~nvhost_gpu() = default; | 42 | nvhost_gpu::~nvhost_gpu() { |
| 43 | events_interface.FreeEvent(sm_exception_breakpoint_int_report_event); | ||
| 44 | events_interface.FreeEvent(sm_exception_breakpoint_pause_report_event); | ||
| 45 | events_interface.FreeEvent(error_notifier_event); | ||
| 46 | syncpoint_manager.FreeSyncpoint(channel_syncpoint); | ||
| 47 | } | ||
| 31 | 48 | ||
| 32 | NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 49 | NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| 33 | std::vector<u8>& output) { | 50 | std::vector<u8>& output) { |
| @@ -167,9 +184,14 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8 | |||
| 167 | params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, | 184 | params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, |
| 168 | params.unk3); | 185 | params.unk3); |
| 169 | 186 | ||
| 170 | channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id); | 187 | if (channel_state->initialized) { |
| 188 | LOG_CRITICAL(Service_NVDRV, "Already allocated!"); | ||
| 189 | return NvResult::AlreadyAllocated; | ||
| 190 | } | ||
| 191 | |||
| 192 | system.GPU().InitChannel(*channel_state); | ||
| 171 | 193 | ||
| 172 | params.fence_out = channel_fence; | 194 | params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint); |
| 173 | 195 | ||
| 174 | std::memcpy(output.data(), ¶ms, output.size()); | 196 | std::memcpy(output.data(), ¶ms, output.size()); |
| 175 | return NvResult::Success; | 197 | return NvResult::Success; |
| @@ -188,39 +210,37 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve | |||
| 188 | 210 | ||
| 189 | static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { | 211 | static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { |
| 190 | return { | 212 | return { |
| 191 | Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, | 213 | Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, |
| 192 | Tegra::SubmissionMode::Increasing), | 214 | Tegra::SubmissionMode::Increasing), |
| 193 | {fence.value}, | 215 | {fence.value}, |
| 194 | Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, | 216 | Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, |
| 195 | Tegra::SubmissionMode::Increasing), | 217 | Tegra::SubmissionMode::Increasing), |
| 196 | BuildFenceAction(Tegra::GPU::FenceOperation::Acquire, fence.id), | 218 | BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Acquire, fence.id), |
| 197 | }; | 219 | }; |
| 198 | } | 220 | } |
| 199 | 221 | ||
| 200 | static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence, | 222 | static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence) { |
| 201 | u32 add_increment) { | ||
| 202 | std::vector<Tegra::CommandHeader> result{ | 223 | std::vector<Tegra::CommandHeader> result{ |
| 203 | Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, | 224 | Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, |
| 204 | Tegra::SubmissionMode::Increasing), | 225 | Tegra::SubmissionMode::Increasing), |
| 205 | {}}; | 226 | {}}; |
| 206 | 227 | ||
| 207 | for (u32 count = 0; count < add_increment; ++count) { | 228 | for (u32 count = 0; count < 2; ++count) { |
| 208 | result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, | 229 | result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, |
| 209 | Tegra::SubmissionMode::Increasing)); | 230 | Tegra::SubmissionMode::Increasing)); |
| 210 | result.emplace_back(BuildFenceAction(Tegra::GPU::FenceOperation::Increment, fence.id)); | 231 | result.emplace_back( |
| 232 | BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id)); | ||
| 211 | } | 233 | } |
| 212 | 234 | ||
| 213 | return result; | 235 | return result; |
| 214 | } | 236 | } |
| 215 | 237 | ||
| 216 | static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence, | 238 | static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence) { |
| 217 | u32 add_increment) { | ||
| 218 | std::vector<Tegra::CommandHeader> result{ | 239 | std::vector<Tegra::CommandHeader> result{ |
| 219 | Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, | 240 | Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1, |
| 220 | Tegra::SubmissionMode::Increasing), | 241 | Tegra::SubmissionMode::Increasing), |
| 221 | {}}; | 242 | {}}; |
| 222 | const std::vector<Tegra::CommandHeader> increment{ | 243 | const std::vector<Tegra::CommandHeader> increment{BuildIncrementCommandList(fence)}; |
| 223 | BuildIncrementCommandList(fence, add_increment)}; | ||
| 224 | 244 | ||
| 225 | result.insert(result.end(), increment.begin(), increment.end()); | 245 | result.insert(result.end(), increment.begin(), increment.end()); |
| 226 | 246 | ||
| @@ -234,33 +254,41 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8> | |||
| 234 | 254 | ||
| 235 | auto& gpu = system.GPU(); | 255 | auto& gpu = system.GPU(); |
| 236 | 256 | ||
| 237 | params.fence_out.id = channel_fence.id; | 257 | std::scoped_lock lock(channel_mutex); |
| 238 | 258 | ||
| 239 | if (params.flags.add_wait.Value() && | 259 | const auto bind_id = channel_state->bind_id; |
| 240 | !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) { | ||
| 241 | gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)}); | ||
| 242 | } | ||
| 243 | 260 | ||
| 244 | if (params.flags.add_increment.Value() || params.flags.increment.Value()) { | 261 | auto& flags = params.flags; |
| 245 | const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0; | 262 | |
| 246 | params.fence_out.value = syncpoint_manager.IncreaseSyncpoint( | 263 | if (flags.fence_wait.Value()) { |
| 247 | params.fence_out.id, params.AddIncrementValue() + increment_value); | 264 | if (flags.increment_value.Value()) { |
| 248 | } else { | 265 | return NvResult::BadParameter; |
| 249 | params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id); | 266 | } |
| 267 | |||
| 268 | if (!syncpoint_manager.IsFenceSignalled(params.fence)) { | ||
| 269 | gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildWaitCommandList(params.fence)}); | ||
| 270 | } | ||
| 250 | } | 271 | } |
| 251 | 272 | ||
| 252 | gpu.PushGPUEntries(std::move(entries)); | 273 | params.fence.id = channel_syncpoint; |
| 274 | |||
| 275 | u32 increment{(flags.fence_increment.Value() != 0 ? 2 : 0) + | ||
| 276 | (flags.increment_value.Value() != 0 ? params.fence.value : 0)}; | ||
| 277 | params.fence.value = syncpoint_manager.IncrementSyncpointMaxExt(channel_syncpoint, increment); | ||
| 278 | gpu.PushGPUEntries(bind_id, std::move(entries)); | ||
| 253 | 279 | ||
| 254 | if (params.flags.add_increment.Value()) { | 280 | if (flags.fence_increment.Value()) { |
| 255 | if (params.flags.suppress_wfi) { | 281 | if (flags.suppress_wfi.Value()) { |
| 256 | gpu.PushGPUEntries(Tegra::CommandList{ | 282 | gpu.PushGPUEntries(bind_id, |
| 257 | BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())}); | 283 | Tegra::CommandList{BuildIncrementCommandList(params.fence)}); |
| 258 | } else { | 284 | } else { |
| 259 | gpu.PushGPUEntries(Tegra::CommandList{ | 285 | gpu.PushGPUEntries(bind_id, |
| 260 | BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())}); | 286 | Tegra::CommandList{BuildIncrementWithWfiCommandList(params.fence)}); |
| 261 | } | 287 | } |
| 262 | } | 288 | } |
| 263 | 289 | ||
| 290 | flags.raw = 0; | ||
| 291 | |||
| 264 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); | 292 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); |
| 265 | return NvResult::Success; | 293 | return NvResult::Success; |
| 266 | } | 294 | } |
| @@ -328,4 +356,19 @@ NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vect | |||
| 328 | return NvResult::Success; | 356 | return NvResult::Success; |
| 329 | } | 357 | } |
| 330 | 358 | ||
| 359 | Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) { | ||
| 360 | switch (event_id) { | ||
| 361 | case 1: | ||
| 362 | return sm_exception_breakpoint_int_report_event; | ||
| 363 | case 2: | ||
| 364 | return sm_exception_breakpoint_pause_report_event; | ||
| 365 | case 3: | ||
| 366 | return error_notifier_event; | ||
| 367 | default: { | ||
| 368 | LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); | ||
| 369 | } | ||
| 370 | } | ||
| 371 | return nullptr; | ||
| 372 | } | ||
| 373 | |||
| 331 | } // namespace Service::Nvidia::Devices | 374 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 8a9f7775a..1e4ecd55b 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h | |||
| @@ -13,17 +13,31 @@ | |||
| 13 | #include "core/hle/service/nvdrv/nvdata.h" | 13 | #include "core/hle/service/nvdrv/nvdata.h" |
| 14 | #include "video_core/dma_pusher.h" | 14 | #include "video_core/dma_pusher.h" |
| 15 | 15 | ||
| 16 | namespace Tegra { | ||
| 17 | namespace Control { | ||
| 18 | struct ChannelState; | ||
| 19 | } | ||
| 20 | } // namespace Tegra | ||
| 21 | |||
| 16 | namespace Service::Nvidia { | 22 | namespace Service::Nvidia { |
| 23 | |||
| 24 | namespace NvCore { | ||
| 25 | class Container; | ||
| 26 | class NvMap; | ||
| 17 | class SyncpointManager; | 27 | class SyncpointManager; |
| 18 | } | 28 | } // namespace NvCore |
| 29 | |||
| 30 | class EventInterface; | ||
| 31 | } // namespace Service::Nvidia | ||
| 19 | 32 | ||
| 20 | namespace Service::Nvidia::Devices { | 33 | namespace Service::Nvidia::Devices { |
| 21 | 34 | ||
| 35 | class nvhost_as_gpu; | ||
| 22 | class nvmap; | 36 | class nvmap; |
| 23 | class nvhost_gpu final : public nvdevice { | 37 | class nvhost_gpu final : public nvdevice { |
| 24 | public: | 38 | public: |
| 25 | explicit nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 39 | explicit nvhost_gpu(Core::System& system_, EventInterface& events_interface_, |
| 26 | SyncpointManager& syncpoint_manager_); | 40 | NvCore::Container& core); |
| 27 | ~nvhost_gpu() override; | 41 | ~nvhost_gpu() override; |
| 28 | 42 | ||
| 29 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 43 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -36,7 +50,10 @@ public: | |||
| 36 | void OnOpen(DeviceFD fd) override; | 50 | void OnOpen(DeviceFD fd) override; |
| 37 | void OnClose(DeviceFD fd) override; | 51 | void OnClose(DeviceFD fd) override; |
| 38 | 52 | ||
| 53 | Kernel::KEvent* QueryEvent(u32 event_id) override; | ||
| 54 | |||
| 39 | private: | 55 | private: |
| 56 | friend class nvhost_as_gpu; | ||
| 40 | enum class CtxObjects : u32_le { | 57 | enum class CtxObjects : u32_le { |
| 41 | Ctx2D = 0x902D, | 58 | Ctx2D = 0x902D, |
| 42 | Ctx3D = 0xB197, | 59 | Ctx3D = 0xB197, |
| @@ -146,17 +163,13 @@ private: | |||
| 146 | u32_le num_entries{}; // number of fence objects being submitted | 163 | u32_le num_entries{}; // number of fence objects being submitted |
| 147 | union { | 164 | union { |
| 148 | u32_le raw; | 165 | u32_le raw; |
| 149 | BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list | 166 | BitField<0, 1, u32_le> fence_wait; // append a wait sync_point to the list |
| 150 | BitField<1, 1, u32_le> add_increment; // append an increment to the list | 167 | BitField<1, 1, u32_le> fence_increment; // append an increment to the list |
| 151 | BitField<2, 1, u32_le> new_hw_format; // mostly ignored | 168 | BitField<2, 1, u32_le> new_hw_format; // mostly ignored |
| 152 | BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt | 169 | BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt |
| 153 | BitField<8, 1, u32_le> increment; // increment the returned fence | 170 | BitField<8, 1, u32_le> increment_value; // increment the returned fence |
| 154 | } flags; | 171 | } flags; |
| 155 | NvFence fence_out{}; // returned new fence object for others to wait on | 172 | NvFence fence{}; // returned new fence object for others to wait on |
| 156 | |||
| 157 | u32 AddIncrementValue() const { | ||
| 158 | return flags.add_increment.Value() << 1; | ||
| 159 | } | ||
| 160 | }; | 173 | }; |
| 161 | static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence), | 174 | static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence), |
| 162 | "IoctlSubmitGpfifo is incorrect size"); | 175 | "IoctlSubmitGpfifo is incorrect size"); |
| @@ -191,9 +204,18 @@ private: | |||
| 191 | NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); | 204 | NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); |
| 192 | NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); | 205 | NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); |
| 193 | 206 | ||
| 194 | std::shared_ptr<nvmap> nvmap_dev; | 207 | EventInterface& events_interface; |
| 195 | SyncpointManager& syncpoint_manager; | 208 | NvCore::Container& core; |
| 196 | NvFence channel_fence; | 209 | NvCore::SyncpointManager& syncpoint_manager; |
| 210 | NvCore::NvMap& nvmap; | ||
| 211 | std::shared_ptr<Tegra::Control::ChannelState> channel_state; | ||
| 212 | u32 channel_syncpoint; | ||
| 213 | std::mutex channel_mutex; | ||
| 214 | |||
| 215 | // Events | ||
| 216 | Kernel::KEvent* sm_exception_breakpoint_int_report_event; | ||
| 217 | Kernel::KEvent* sm_exception_breakpoint_pause_report_event; | ||
| 218 | Kernel::KEvent* error_notifier_event; | ||
| 197 | }; | 219 | }; |
| 198 | 220 | ||
| 199 | } // namespace Service::Nvidia::Devices | 221 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index a7385fce8..1703f9cc3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp | |||
| @@ -5,14 +5,14 @@ | |||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 8 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" | 9 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" |
| 9 | #include "video_core/renderer_base.h" | 10 | #include "video_core/renderer_base.h" |
| 10 | 11 | ||
| 11 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| 12 | 13 | ||
| 13 | nvhost_nvdec::nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 14 | nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_) |
| 14 | SyncpointManager& syncpoint_manager_) | 15 | : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {} |
| 15 | : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} | ||
| 16 | nvhost_nvdec::~nvhost_nvdec() = default; | 16 | nvhost_nvdec::~nvhost_nvdec() = default; |
| 17 | 17 | ||
| 18 | NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 18 | NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -21,8 +21,9 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& | |||
| 21 | case 0x0: | 21 | case 0x0: |
| 22 | switch (command.cmd) { | 22 | switch (command.cmd) { |
| 23 | case 0x1: { | 23 | case 0x1: { |
| 24 | if (!fd_to_id.contains(fd)) { | 24 | auto& host1x_file = core.Host1xDeviceFile(); |
| 25 | fd_to_id[fd] = next_id++; | 25 | if (!host1x_file.fd_to_id.contains(fd)) { |
| 26 | host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++; | ||
| 26 | } | 27 | } |
| 27 | return Submit(fd, input, output); | 28 | return Submit(fd, input, output); |
| 28 | } | 29 | } |
| @@ -73,8 +74,9 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) { | |||
| 73 | 74 | ||
| 74 | void nvhost_nvdec::OnClose(DeviceFD fd) { | 75 | void nvhost_nvdec::OnClose(DeviceFD fd) { |
| 75 | LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); | 76 | LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); |
| 76 | const auto iter = fd_to_id.find(fd); | 77 | auto& host1x_file = core.Host1xDeviceFile(); |
| 77 | if (iter != fd_to_id.end()) { | 78 | const auto iter = host1x_file.fd_to_id.find(fd); |
| 79 | if (iter != host1x_file.fd_to_id.end()) { | ||
| 78 | system.GPU().ClearCdmaInstance(iter->second); | 80 | system.GPU().ClearCdmaInstance(iter->second); |
| 79 | } | 81 | } |
| 80 | system.AudioCore().SetNVDECActive(false); | 82 | system.AudioCore().SetNVDECActive(false); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 29b3e6a36..c1b4e53e8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h | |||
| @@ -10,8 +10,7 @@ namespace Service::Nvidia::Devices { | |||
| 10 | 10 | ||
| 11 | class nvhost_nvdec final : public nvhost_nvdec_common { | 11 | class nvhost_nvdec final : public nvhost_nvdec_common { |
| 12 | public: | 12 | public: |
| 13 | explicit nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 13 | explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core); |
| 14 | SyncpointManager& syncpoint_manager_); | ||
| 15 | ~nvhost_nvdec() override; | 14 | ~nvhost_nvdec() override; |
| 16 | 15 | ||
| 17 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 16 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -23,9 +22,6 @@ public: | |||
| 23 | 22 | ||
| 24 | void OnOpen(DeviceFD fd) override; | 23 | void OnOpen(DeviceFD fd) override; |
| 25 | void OnClose(DeviceFD fd) override; | 24 | void OnClose(DeviceFD fd) override; |
| 26 | |||
| 27 | private: | ||
| 28 | u32 next_id{}; | ||
| 29 | }; | 25 | }; |
| 30 | 26 | ||
| 31 | } // namespace Service::Nvidia::Devices | 27 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 8b2cd9bf1..99eede702 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp | |||
| @@ -8,10 +8,12 @@ | |||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 12 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 13 | #include "core/hle/service/nvdrv/core/syncpoint_manager.h" | ||
| 11 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" | 14 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" |
| 12 | #include "core/hle/service/nvdrv/devices/nvmap.h" | ||
| 13 | #include "core/hle/service/nvdrv/syncpoint_manager.h" | ||
| 14 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| 16 | #include "video_core/host1x/host1x.h" | ||
| 15 | #include "video_core/memory_manager.h" | 17 | #include "video_core/memory_manager.h" |
| 16 | #include "video_core/renderer_base.h" | 18 | #include "video_core/renderer_base.h" |
| 17 | 19 | ||
| @@ -44,10 +46,22 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s | |||
| 44 | } | 46 | } |
| 45 | } // Anonymous namespace | 47 | } // Anonymous namespace |
| 46 | 48 | ||
| 47 | nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 49 | nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_, |
| 48 | SyncpointManager& syncpoint_manager_) | 50 | NvCore::ChannelType channel_type_) |
| 49 | : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} {} | 51 | : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, |
| 50 | nvhost_nvdec_common::~nvhost_nvdec_common() = default; | 52 | nvmap{core.GetNvMapFile()}, channel_type{channel_type_} { |
| 53 | auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated; | ||
| 54 | if (syncpts_accumulated.empty()) { | ||
| 55 | channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); | ||
| 56 | } else { | ||
| 57 | channel_syncpoint = syncpts_accumulated.front(); | ||
| 58 | syncpts_accumulated.pop_front(); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | nvhost_nvdec_common::~nvhost_nvdec_common() { | ||
| 63 | core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); | ||
| 64 | } | ||
| 51 | 65 | ||
| 52 | NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { | 66 | NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { |
| 53 | IoctlSetNvmapFD params{}; | 67 | IoctlSetNvmapFD params{}; |
| @@ -84,16 +98,16 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input, | |||
| 84 | for (std::size_t i = 0; i < syncpt_increments.size(); i++) { | 98 | for (std::size_t i = 0; i < syncpt_increments.size(); i++) { |
| 85 | const SyncptIncr& syncpt_incr = syncpt_increments[i]; | 99 | const SyncptIncr& syncpt_incr = syncpt_increments[i]; |
| 86 | fence_thresholds[i] = | 100 | fence_thresholds[i] = |
| 87 | syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments); | 101 | syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments); |
| 88 | } | 102 | } |
| 89 | } | 103 | } |
| 90 | for (const auto& cmd_buffer : command_buffers) { | 104 | for (const auto& cmd_buffer : command_buffers) { |
| 91 | const auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); | 105 | const auto object = nvmap.GetHandle(cmd_buffer.memory_id); |
| 92 | ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); | 106 | ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); |
| 93 | Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); | 107 | Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); |
| 94 | system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), | 108 | system.Memory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), |
| 95 | cmdlist.size() * sizeof(u32)); | 109 | cmdlist.size() * sizeof(u32)); |
| 96 | gpu.PushCommandBuffer(fd_to_id[fd], cmdlist); | 110 | gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); |
| 97 | } | 111 | } |
| 98 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); | 112 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); |
| 99 | // Some games expect command_buffers to be written back | 113 | // Some games expect command_buffers to be written back |
| @@ -112,10 +126,8 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve | |||
| 112 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); | 126 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); |
| 113 | LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); | 127 | LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); |
| 114 | 128 | ||
| 115 | if (device_syncpoints[params.param] == 0 && system.GPU().UseNvdec()) { | 129 | // const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast<u32>(channel_type)]}; |
| 116 | device_syncpoints[params.param] = syncpoint_manager.AllocateSyncpoint(); | 130 | params.value = channel_syncpoint; |
| 117 | } | ||
| 118 | params.value = device_syncpoints[params.param]; | ||
| 119 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); | 131 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); |
| 120 | 132 | ||
| 121 | return NvResult::Success; | 133 | return NvResult::Success; |
| @@ -123,6 +135,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve | |||
| 123 | 135 | ||
| 124 | NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { | 136 | NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { |
| 125 | IoctlGetWaitbase params{}; | 137 | IoctlGetWaitbase params{}; |
| 138 | LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); | ||
| 126 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); | 139 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); |
| 127 | params.value = 0; // Seems to be hard coded at 0 | 140 | params.value = 0; // Seems to be hard coded at 0 |
| 128 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); | 141 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); |
| @@ -136,28 +149,8 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto | |||
| 136 | 149 | ||
| 137 | SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); | 150 | SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); |
| 138 | 151 | ||
| 139 | auto& gpu = system.GPU(); | ||
| 140 | |||
| 141 | for (auto& cmd_buffer : cmd_buffer_handles) { | 152 | for (auto& cmd_buffer : cmd_buffer_handles) { |
| 142 | auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)}; | 153 | cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle); |
| 143 | if (!object) { | ||
| 144 | LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle); | ||
| 145 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 146 | return NvResult::InvalidState; | ||
| 147 | } | ||
| 148 | if (object->dma_map_addr == 0) { | ||
| 149 | // NVDEC and VIC memory is in the 32-bit address space | ||
| 150 | // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space | ||
| 151 | const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size); | ||
| 152 | object->dma_map_addr = static_cast<u32>(low_addr); | ||
| 153 | // Ensure that the dma_map_addr is indeed in the lower 32-bit address space. | ||
| 154 | ASSERT(object->dma_map_addr == low_addr); | ||
| 155 | } | ||
| 156 | if (!object->dma_map_addr) { | ||
| 157 | LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size); | ||
| 158 | } else { | ||
| 159 | cmd_buffer.map_address = object->dma_map_addr; | ||
| 160 | } | ||
| 161 | } | 154 | } |
| 162 | std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); | 155 | std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); |
| 163 | std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), | 156 | std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), |
| @@ -167,11 +160,16 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto | |||
| 167 | } | 160 | } |
| 168 | 161 | ||
| 169 | NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | 162 | NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { |
| 170 | // This is intntionally stubbed. | 163 | IoctlMapBuffer params{}; |
| 171 | // Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame | 164 | std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); |
| 172 | // addresses, and risk invalidating data before the async GPU thread is done with it | 165 | std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); |
| 166 | |||
| 167 | SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); | ||
| 168 | for (auto& cmd_buffer : cmd_buffer_handles) { | ||
| 169 | nvmap.UnpinHandle(cmd_buffer.map_handle); | ||
| 170 | } | ||
| 171 | |||
| 173 | std::memset(output.data(), 0, output.size()); | 172 | std::memset(output.data(), 0, output.size()); |
| 174 | LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); | ||
| 175 | return NvResult::Success; | 173 | return NvResult::Success; |
| 176 | } | 174 | } |
| 177 | 175 | ||
| @@ -182,4 +180,9 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, | |||
| 182 | return NvResult::Success; | 180 | return NvResult::Success; |
| 183 | } | 181 | } |
| 184 | 182 | ||
| 183 | Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) { | ||
| 184 | LOG_CRITICAL(Service_NVDRV, "Unknown HOSTX1 Event {}", event_id); | ||
| 185 | return nullptr; | ||
| 186 | } | ||
| 187 | |||
| 185 | } // namespace Service::Nvidia::Devices | 188 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 12d39946d..fe76100c8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h | |||
| @@ -3,21 +3,26 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <deque> | ||
| 6 | #include <vector> | 7 | #include <vector> |
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | #include "common/swap.h" | 9 | #include "common/swap.h" |
| 10 | #include "core/hle/service/nvdrv/core/syncpoint_manager.h" | ||
| 9 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 11 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| 10 | 12 | ||
| 11 | namespace Service::Nvidia { | 13 | namespace Service::Nvidia { |
| 12 | class SyncpointManager; | 14 | |
| 15 | namespace NvCore { | ||
| 16 | class Container; | ||
| 17 | class NvMap; | ||
| 18 | } // namespace NvCore | ||
| 13 | 19 | ||
| 14 | namespace Devices { | 20 | namespace Devices { |
| 15 | class nvmap; | ||
| 16 | 21 | ||
| 17 | class nvhost_nvdec_common : public nvdevice { | 22 | class nvhost_nvdec_common : public nvdevice { |
| 18 | public: | 23 | public: |
| 19 | explicit nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 24 | explicit nvhost_nvdec_common(Core::System& system_, NvCore::Container& core, |
| 20 | SyncpointManager& syncpoint_manager_); | 25 | NvCore::ChannelType channel_type); |
| 21 | ~nvhost_nvdec_common() override; | 26 | ~nvhost_nvdec_common() override; |
| 22 | 27 | ||
| 23 | protected: | 28 | protected: |
| @@ -110,11 +115,15 @@ protected: | |||
| 110 | NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); | 115 | NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); |
| 111 | NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); | 116 | NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); |
| 112 | 117 | ||
| 113 | std::unordered_map<DeviceFD, u32> fd_to_id{}; | 118 | Kernel::KEvent* QueryEvent(u32 event_id) override; |
| 119 | |||
| 120 | u32 channel_syncpoint; | ||
| 114 | s32_le nvmap_fd{}; | 121 | s32_le nvmap_fd{}; |
| 115 | u32_le submit_timeout{}; | 122 | u32_le submit_timeout{}; |
| 116 | std::shared_ptr<nvmap> nvmap_dev; | 123 | NvCore::Container& core; |
| 117 | SyncpointManager& syncpoint_manager; | 124 | NvCore::SyncpointManager& syncpoint_manager; |
| 125 | NvCore::NvMap& nvmap; | ||
| 126 | NvCore::ChannelType channel_type; | ||
| 118 | std::array<u32, MaxSyncPoints> device_syncpoints{}; | 127 | std::array<u32, MaxSyncPoints> device_syncpoints{}; |
| 119 | }; | 128 | }; |
| 120 | }; // namespace Devices | 129 | }; // namespace Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index f58e8bada..73f97136e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp | |||
| @@ -4,13 +4,14 @@ | |||
| 4 | #include "common/assert.h" | 4 | #include "common/assert.h" |
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 7 | #include "core/hle/service/nvdrv/devices/nvhost_vic.h" | 8 | #include "core/hle/service/nvdrv/devices/nvhost_vic.h" |
| 8 | #include "video_core/renderer_base.h" | 9 | #include "video_core/renderer_base.h" |
| 9 | 10 | ||
| 10 | namespace Service::Nvidia::Devices { | 11 | namespace Service::Nvidia::Devices { |
| 11 | nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 12 | |
| 12 | SyncpointManager& syncpoint_manager_) | 13 | nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_) |
| 13 | : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} | 14 | : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::VIC} {} |
| 14 | 15 | ||
| 15 | nvhost_vic::~nvhost_vic() = default; | 16 | nvhost_vic::~nvhost_vic() = default; |
| 16 | 17 | ||
| @@ -19,11 +20,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i | |||
| 19 | switch (command.group) { | 20 | switch (command.group) { |
| 20 | case 0x0: | 21 | case 0x0: |
| 21 | switch (command.cmd) { | 22 | switch (command.cmd) { |
| 22 | case 0x1: | 23 | case 0x1: { |
| 23 | if (!fd_to_id.contains(fd)) { | 24 | auto& host1x_file = core.Host1xDeviceFile(); |
| 24 | fd_to_id[fd] = next_id++; | 25 | if (!host1x_file.fd_to_id.contains(fd)) { |
| 26 | host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++; | ||
| 25 | } | 27 | } |
| 26 | return Submit(fd, input, output); | 28 | return Submit(fd, input, output); |
| 29 | } | ||
| 27 | case 0x2: | 30 | case 0x2: |
| 28 | return GetSyncpoint(input, output); | 31 | return GetSyncpoint(input, output); |
| 29 | case 0x3: | 32 | case 0x3: |
| @@ -67,8 +70,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i | |||
| 67 | void nvhost_vic::OnOpen(DeviceFD fd) {} | 70 | void nvhost_vic::OnOpen(DeviceFD fd) {} |
| 68 | 71 | ||
| 69 | void nvhost_vic::OnClose(DeviceFD fd) { | 72 | void nvhost_vic::OnClose(DeviceFD fd) { |
| 70 | const auto iter = fd_to_id.find(fd); | 73 | auto& host1x_file = core.Host1xDeviceFile(); |
| 71 | if (iter != fd_to_id.end()) { | 74 | const auto iter = host1x_file.fd_to_id.find(fd); |
| 75 | if (iter != host1x_file.fd_to_id.end()) { | ||
| 72 | system.GPU().ClearCdmaInstance(iter->second); | 76 | system.GPU().ClearCdmaInstance(iter->second); |
| 73 | } | 77 | } |
| 74 | } | 78 | } |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index b41b195ae..f164caafb 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h | |||
| @@ -9,8 +9,7 @@ namespace Service::Nvidia::Devices { | |||
| 9 | 9 | ||
| 10 | class nvhost_vic final : public nvhost_nvdec_common { | 10 | class nvhost_vic final : public nvhost_nvdec_common { |
| 11 | public: | 11 | public: |
| 12 | explicit nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 12 | explicit nvhost_vic(Core::System& system_, NvCore::Container& core); |
| 13 | SyncpointManager& syncpoint_manager_); | ||
| 14 | ~nvhost_vic(); | 13 | ~nvhost_vic(); |
| 15 | 14 | ||
| 16 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 15 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -22,8 +21,5 @@ public: | |||
| 22 | 21 | ||
| 23 | void OnOpen(DeviceFD fd) override; | 22 | void OnOpen(DeviceFD fd) override; |
| 24 | void OnClose(DeviceFD fd) override; | 23 | void OnClose(DeviceFD fd) override; |
| 25 | |||
| 26 | private: | ||
| 27 | u32 next_id{}; | ||
| 28 | }; | 24 | }; |
| 29 | } // namespace Service::Nvidia::Devices | 25 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index d8518149d..ddf273b5e 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp | |||
| @@ -2,19 +2,26 @@ | |||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include <algorithm> |
| 5 | #include <bit> | ||
| 5 | #include <cstring> | 6 | #include <cstring> |
| 6 | 7 | ||
| 8 | #include "common/alignment.h" | ||
| 7 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "core/core.h" | ||
| 12 | #include "core/hle/kernel/k_page_table.h" | ||
| 13 | #include "core/hle/kernel/k_process.h" | ||
| 14 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 15 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 9 | #include "core/hle/service/nvdrv/devices/nvmap.h" | 16 | #include "core/hle/service/nvdrv/devices/nvmap.h" |
| 17 | #include "core/memory.h" | ||
| 18 | |||
| 19 | using Core::Memory::YUZU_PAGESIZE; | ||
| 10 | 20 | ||
| 11 | namespace Service::Nvidia::Devices { | 21 | namespace Service::Nvidia::Devices { |
| 12 | 22 | ||
| 13 | nvmap::nvmap(Core::System& system_) : nvdevice{system_} { | 23 | nvmap::nvmap(Core::System& system_, NvCore::Container& container_) |
| 14 | // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to | 24 | : nvdevice{system_}, container{container_}, file{container.GetNvMapFile()} {} |
| 15 | // represent this. | ||
| 16 | CreateObject(0); | ||
| 17 | } | ||
| 18 | 25 | ||
| 19 | nvmap::~nvmap() = default; | 26 | nvmap::~nvmap() = default; |
| 20 | 27 | ||
| @@ -62,39 +69,21 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | |||
| 62 | void nvmap::OnOpen(DeviceFD fd) {} | 69 | void nvmap::OnOpen(DeviceFD fd) {} |
| 63 | void nvmap::OnClose(DeviceFD fd) {} | 70 | void nvmap::OnClose(DeviceFD fd) {} |
| 64 | 71 | ||
| 65 | VAddr nvmap::GetObjectAddress(u32 handle) const { | ||
| 66 | auto object = GetObject(handle); | ||
| 67 | ASSERT(object); | ||
| 68 | ASSERT(object->status == Object::Status::Allocated); | ||
| 69 | return object->addr; | ||
| 70 | } | ||
| 71 | |||
| 72 | u32 nvmap::CreateObject(u32 size) { | ||
| 73 | // Create a new nvmap object and obtain a handle to it. | ||
| 74 | auto object = std::make_shared<Object>(); | ||
| 75 | object->id = next_id++; | ||
| 76 | object->size = size; | ||
| 77 | object->status = Object::Status::Created; | ||
| 78 | object->refcount = 1; | ||
| 79 | |||
| 80 | const u32 handle = next_handle++; | ||
| 81 | |||
| 82 | handles.insert_or_assign(handle, std::move(object)); | ||
| 83 | |||
| 84 | return handle; | ||
| 85 | } | ||
| 86 | |||
| 87 | NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { | 72 | NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { |
| 88 | IocCreateParams params; | 73 | IocCreateParams params; |
| 89 | std::memcpy(¶ms, input.data(), sizeof(params)); | 74 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 90 | LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); | 75 | LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); |
| 91 | 76 | ||
| 92 | if (!params.size) { | 77 | std::shared_ptr<NvCore::NvMap::Handle> handle_description{}; |
| 93 | LOG_ERROR(Service_NVDRV, "Size is 0"); | 78 | auto result = |
| 94 | return NvResult::BadValue; | 79 | file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description); |
| 80 | if (result != NvResult::Success) { | ||
| 81 | LOG_CRITICAL(Service_NVDRV, "Failed to create Object"); | ||
| 82 | return result; | ||
| 95 | } | 83 | } |
| 96 | 84 | handle_description->orig_size = params.size; // Orig size is the unaligned size | |
| 97 | params.handle = CreateObject(params.size); | 85 | params.handle = handle_description->id; |
| 86 | LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size); | ||
| 98 | 87 | ||
| 99 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 88 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| 100 | return NvResult::Success; | 89 | return NvResult::Success; |
| @@ -103,63 +92,68 @@ NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) | |||
| 103 | NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { | 92 | NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { |
| 104 | IocAllocParams params; | 93 | IocAllocParams params; |
| 105 | std::memcpy(¶ms, input.data(), sizeof(params)); | 94 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 106 | LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); | 95 | LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); |
| 107 | 96 | ||
| 108 | if (!params.handle) { | 97 | if (!params.handle) { |
| 109 | LOG_ERROR(Service_NVDRV, "Handle is 0"); | 98 | LOG_CRITICAL(Service_NVDRV, "Handle is 0"); |
| 110 | return NvResult::BadValue; | 99 | return NvResult::BadValue; |
| 111 | } | 100 | } |
| 112 | 101 | ||
| 113 | if ((params.align - 1) & params.align) { | 102 | if ((params.align - 1) & params.align) { |
| 114 | LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); | 103 | LOG_CRITICAL(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); |
| 115 | return NvResult::BadValue; | 104 | return NvResult::BadValue; |
| 116 | } | 105 | } |
| 117 | 106 | ||
| 118 | const u32 min_alignment = 0x1000; | 107 | // Force page size alignment at a minimum |
| 119 | if (params.align < min_alignment) { | 108 | if (params.align < YUZU_PAGESIZE) { |
| 120 | params.align = min_alignment; | 109 | params.align = YUZU_PAGESIZE; |
| 121 | } | 110 | } |
| 122 | 111 | ||
| 123 | auto object = GetObject(params.handle); | 112 | auto handle_description{file.GetHandle(params.handle)}; |
| 124 | if (!object) { | 113 | if (!handle_description) { |
| 125 | LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); | 114 | LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); |
| 126 | return NvResult::BadValue; | 115 | return NvResult::BadValue; |
| 127 | } | 116 | } |
| 128 | 117 | ||
| 129 | if (object->status == Object::Status::Allocated) { | 118 | if (handle_description->allocated) { |
| 130 | LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); | 119 | LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); |
| 131 | return NvResult::InsufficientMemory; | 120 | return NvResult::InsufficientMemory; |
| 132 | } | 121 | } |
| 133 | 122 | ||
| 134 | object->flags = params.flags; | 123 | const auto result = |
| 135 | object->align = params.align; | 124 | handle_description->Alloc(params.flags, params.align, params.kind, params.address); |
| 136 | object->kind = params.kind; | 125 | if (result != NvResult::Success) { |
| 137 | object->addr = params.addr; | 126 | LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle); |
| 138 | object->status = Object::Status::Allocated; | 127 | return result; |
| 139 | 128 | } | |
| 129 | ASSERT(system.CurrentProcess() | ||
| 130 | ->PageTable() | ||
| 131 | .LockForDeviceAddressSpace(handle_description->address, handle_description->size) | ||
| 132 | .IsSuccess()); | ||
| 140 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 133 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| 141 | return NvResult::Success; | 134 | return result; |
| 142 | } | 135 | } |
| 143 | 136 | ||
| 144 | NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { | 137 | NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { |
| 145 | IocGetIdParams params; | 138 | IocGetIdParams params; |
| 146 | std::memcpy(¶ms, input.data(), sizeof(params)); | 139 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 147 | 140 | ||
| 148 | LOG_WARNING(Service_NVDRV, "called"); | 141 | LOG_DEBUG(Service_NVDRV, "called"); |
| 149 | 142 | ||
| 143 | // See the comment in FromId for extra info on this function | ||
| 150 | if (!params.handle) { | 144 | if (!params.handle) { |
| 151 | LOG_ERROR(Service_NVDRV, "Handle is zero"); | 145 | LOG_CRITICAL(Service_NVDRV, "Error!"); |
| 152 | return NvResult::BadValue; | 146 | return NvResult::BadValue; |
| 153 | } | 147 | } |
| 154 | 148 | ||
| 155 | auto object = GetObject(params.handle); | 149 | auto handle_description{file.GetHandle(params.handle)}; |
| 156 | if (!object) { | 150 | if (!handle_description) { |
| 157 | LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); | 151 | LOG_CRITICAL(Service_NVDRV, "Error!"); |
| 158 | return NvResult::BadValue; | 152 | return NvResult::AccessDenied; // This will always return EPERM irrespective of if the |
| 153 | // handle exists or not | ||
| 159 | } | 154 | } |
| 160 | 155 | ||
| 161 | params.id = object->id; | 156 | params.id = handle_description->id; |
| 162 | |||
| 163 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 157 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| 164 | return NvResult::Success; | 158 | return NvResult::Success; |
| 165 | } | 159 | } |
| @@ -168,26 +162,29 @@ NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) | |||
| 168 | IocFromIdParams params; | 162 | IocFromIdParams params; |
| 169 | std::memcpy(¶ms, input.data(), sizeof(params)); | 163 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 170 | 164 | ||
| 171 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | 165 | LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id); |
| 172 | 166 | ||
| 173 | auto itr = std::find_if(handles.begin(), handles.end(), | 167 | // Handles and IDs are always the same value in nvmap however IDs can be used globally given the |
| 174 | [&](const auto& entry) { return entry.second->id == params.id; }); | 168 | // right permissions. |
| 175 | if (itr == handles.end()) { | 169 | // Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and |
| 176 | LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); | 170 | // so this function just does simple validation and passes through the handle id. |
| 171 | if (!params.id) { | ||
| 172 | LOG_CRITICAL(Service_NVDRV, "Zero Id is invalid!"); | ||
| 177 | return NvResult::BadValue; | 173 | return NvResult::BadValue; |
| 178 | } | 174 | } |
| 179 | 175 | ||
| 180 | auto& object = itr->second; | 176 | auto handle_description{file.GetHandle(params.id)}; |
| 181 | if (object->status != Object::Status::Allocated) { | 177 | if (!handle_description) { |
| 182 | LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); | 178 | LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); |
| 183 | return NvResult::BadValue; | 179 | return NvResult::BadValue; |
| 184 | } | 180 | } |
| 185 | 181 | ||
| 186 | itr->second->refcount++; | 182 | auto result = handle_description->Duplicate(false); |
| 187 | 183 | if (result != NvResult::Success) { | |
| 188 | // Return the existing handle instead of creating a new one. | 184 | LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); |
| 189 | params.handle = itr->first; | 185 | return result; |
| 190 | 186 | } | |
| 187 | params.handle = handle_description->id; | ||
| 191 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 188 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| 192 | return NvResult::Success; | 189 | return NvResult::Success; |
| 193 | } | 190 | } |
| @@ -198,35 +195,43 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) | |||
| 198 | IocParamParams params; | 195 | IocParamParams params; |
| 199 | std::memcpy(¶ms, input.data(), sizeof(params)); | 196 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 200 | 197 | ||
| 201 | LOG_DEBUG(Service_NVDRV, "(STUBBED) called type={}", params.param); | 198 | LOG_DEBUG(Service_NVDRV, "called type={}", params.param); |
| 202 | 199 | ||
| 203 | auto object = GetObject(params.handle); | 200 | if (!params.handle) { |
| 204 | if (!object) { | 201 | LOG_CRITICAL(Service_NVDRV, "Invalid handle!"); |
| 205 | LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); | ||
| 206 | return NvResult::BadValue; | 202 | return NvResult::BadValue; |
| 207 | } | 203 | } |
| 208 | 204 | ||
| 209 | if (object->status != Object::Status::Allocated) { | 205 | auto handle_description{file.GetHandle(params.handle)}; |
| 210 | LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); | 206 | if (!handle_description) { |
| 207 | LOG_CRITICAL(Service_NVDRV, "Not registered handle!"); | ||
| 211 | return NvResult::BadValue; | 208 | return NvResult::BadValue; |
| 212 | } | 209 | } |
| 213 | 210 | ||
| 214 | switch (static_cast<ParamTypes>(params.param)) { | 211 | switch (params.param) { |
| 215 | case ParamTypes::Size: | 212 | case HandleParameterType::Size: |
| 216 | params.result = object->size; | 213 | params.result = static_cast<u32_le>(handle_description->orig_size); |
| 214 | break; | ||
| 215 | case HandleParameterType::Alignment: | ||
| 216 | params.result = static_cast<u32_le>(handle_description->align); | ||
| 217 | break; | 217 | break; |
| 218 | case ParamTypes::Alignment: | 218 | case HandleParameterType::Base: |
| 219 | params.result = object->align; | 219 | params.result = static_cast<u32_le>(-22); // posix EINVAL |
| 220 | break; | 220 | break; |
| 221 | case ParamTypes::Heap: | 221 | case HandleParameterType::Heap: |
| 222 | // TODO(Subv): Seems to be a hardcoded value? | 222 | if (handle_description->allocated) |
| 223 | params.result = 0x40000000; | 223 | params.result = 0x40000000; |
| 224 | else | ||
| 225 | params.result = 0; | ||
| 224 | break; | 226 | break; |
| 225 | case ParamTypes::Kind: | 227 | case HandleParameterType::Kind: |
| 226 | params.result = object->kind; | 228 | params.result = handle_description->kind; |
| 229 | break; | ||
| 230 | case HandleParameterType::IsSharedMemMapped: | ||
| 231 | params.result = handle_description->is_shared_mem_mapped; | ||
| 227 | break; | 232 | break; |
| 228 | default: | 233 | default: |
| 229 | UNIMPLEMENTED(); | 234 | return NvResult::BadValue; |
| 230 | } | 235 | } |
| 231 | 236 | ||
| 232 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 237 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| @@ -234,46 +239,29 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) | |||
| 234 | } | 239 | } |
| 235 | 240 | ||
| 236 | NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { | 241 | NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { |
| 237 | // TODO(Subv): These flags are unconfirmed. | ||
| 238 | enum FreeFlags { | ||
| 239 | Freed = 0, | ||
| 240 | NotFreedYet = 1, | ||
| 241 | }; | ||
| 242 | |||
| 243 | IocFreeParams params; | 242 | IocFreeParams params; |
| 244 | std::memcpy(¶ms, input.data(), sizeof(params)); | 243 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 245 | 244 | ||
| 246 | LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); | 245 | LOG_DEBUG(Service_NVDRV, "called"); |
| 247 | 246 | ||
| 248 | auto itr = handles.find(params.handle); | 247 | if (!params.handle) { |
| 249 | if (itr == handles.end()) { | 248 | LOG_CRITICAL(Service_NVDRV, "Handle null freed?"); |
| 250 | LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); | 249 | return NvResult::Success; |
| 251 | return NvResult::BadValue; | ||
| 252 | } | ||
| 253 | if (!itr->second->refcount) { | ||
| 254 | LOG_ERROR( | ||
| 255 | Service_NVDRV, | ||
| 256 | "There is no references to this object. The object is already freed. handle={:08X}", | ||
| 257 | params.handle); | ||
| 258 | return NvResult::BadValue; | ||
| 259 | } | 250 | } |
| 260 | 251 | ||
| 261 | itr->second->refcount--; | 252 | if (auto freeInfo{file.FreeHandle(params.handle, false)}) { |
| 262 | 253 | ASSERT(system.CurrentProcess() | |
| 263 | params.size = itr->second->size; | 254 | ->PageTable() |
| 264 | 255 | .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) | |
| 265 | if (itr->second->refcount == 0) { | 256 | .IsSuccess()); |
| 266 | params.flags = Freed; | 257 | params.address = freeInfo->address; |
| 267 | // The address of the nvmap is written to the output if we're finally freeing it, otherwise | 258 | params.size = static_cast<u32>(freeInfo->size); |
| 268 | // 0 is written. | 259 | params.flags.raw = 0; |
| 269 | params.address = itr->second->addr; | 260 | params.flags.map_uncached.Assign(freeInfo->was_uncached); |
| 270 | } else { | 261 | } else { |
| 271 | params.flags = NotFreedYet; | 262 | // This is possible when there's internel dups or other duplicates. |
| 272 | params.address = 0; | ||
| 273 | } | 263 | } |
| 274 | 264 | ||
| 275 | handles.erase(params.handle); | ||
| 276 | |||
| 277 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 265 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| 278 | return NvResult::Success; | 266 | return NvResult::Success; |
| 279 | } | 267 | } |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index d5360d6e5..e9bfd0358 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h | |||
| @@ -9,15 +9,23 @@ | |||
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 12 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 12 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 13 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| 13 | 14 | ||
| 15 | namespace Service::Nvidia::NvCore { | ||
| 16 | class Container; | ||
| 17 | } // namespace Service::Nvidia::NvCore | ||
| 18 | |||
| 14 | namespace Service::Nvidia::Devices { | 19 | namespace Service::Nvidia::Devices { |
| 15 | 20 | ||
| 16 | class nvmap final : public nvdevice { | 21 | class nvmap final : public nvdevice { |
| 17 | public: | 22 | public: |
| 18 | explicit nvmap(Core::System& system_); | 23 | explicit nvmap(Core::System& system_, NvCore::Container& container); |
| 19 | ~nvmap() override; | 24 | ~nvmap() override; |
| 20 | 25 | ||
| 26 | nvmap(const nvmap&) = delete; | ||
| 27 | nvmap& operator=(const nvmap&) = delete; | ||
| 28 | |||
| 21 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 29 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| 22 | std::vector<u8>& output) override; | 30 | std::vector<u8>& output) override; |
| 23 | NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 31 | NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -28,31 +36,15 @@ public: | |||
| 28 | void OnOpen(DeviceFD fd) override; | 36 | void OnOpen(DeviceFD fd) override; |
| 29 | void OnClose(DeviceFD fd) override; | 37 | void OnClose(DeviceFD fd) override; |
| 30 | 38 | ||
| 31 | /// Returns the allocated address of an nvmap object given its handle. | 39 | enum class HandleParameterType : u32_le { |
| 32 | VAddr GetObjectAddress(u32 handle) const; | 40 | Size = 1, |
| 33 | 41 | Alignment = 2, | |
| 34 | /// Represents an nvmap object. | 42 | Base = 3, |
| 35 | struct Object { | 43 | Heap = 4, |
| 36 | enum class Status { Created, Allocated }; | 44 | Kind = 5, |
| 37 | u32 id; | 45 | IsSharedMemMapped = 6 |
| 38 | u32 size; | ||
| 39 | u32 flags; | ||
| 40 | u32 align; | ||
| 41 | u8 kind; | ||
| 42 | VAddr addr; | ||
| 43 | Status status; | ||
| 44 | u32 refcount; | ||
| 45 | u32 dma_map_addr; | ||
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | std::shared_ptr<Object> GetObject(u32 handle) const { | ||
| 49 | auto itr = handles.find(handle); | ||
| 50 | if (itr != handles.end()) { | ||
| 51 | return itr->second; | ||
| 52 | } | ||
| 53 | return {}; | ||
| 54 | } | ||
| 55 | |||
| 56 | private: | 48 | private: |
| 57 | /// Id to use for the next handle that is created. | 49 | /// Id to use for the next handle that is created. |
| 58 | u32 next_handle = 0; | 50 | u32 next_handle = 0; |
| @@ -60,9 +52,6 @@ private: | |||
| 60 | /// Id to use for the next object that is created. | 52 | /// Id to use for the next object that is created. |
| 61 | u32 next_id = 0; | 53 | u32 next_id = 0; |
| 62 | 54 | ||
| 63 | /// Mapping of currently allocated handles to the objects they represent. | ||
| 64 | std::unordered_map<u32, std::shared_ptr<Object>> handles; | ||
| 65 | |||
| 66 | struct IocCreateParams { | 55 | struct IocCreateParams { |
| 67 | // Input | 56 | // Input |
| 68 | u32_le size{}; | 57 | u32_le size{}; |
| @@ -83,11 +72,11 @@ private: | |||
| 83 | // Input | 72 | // Input |
| 84 | u32_le handle{}; | 73 | u32_le handle{}; |
| 85 | u32_le heap_mask{}; | 74 | u32_le heap_mask{}; |
| 86 | u32_le flags{}; | 75 | NvCore::NvMap::Handle::Flags flags{}; |
| 87 | u32_le align{}; | 76 | u32_le align{}; |
| 88 | u8 kind{}; | 77 | u8 kind{}; |
| 89 | INSERT_PADDING_BYTES(7); | 78 | INSERT_PADDING_BYTES(7); |
| 90 | u64_le addr{}; | 79 | u64_le address{}; |
| 91 | }; | 80 | }; |
| 92 | static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size"); | 81 | static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size"); |
| 93 | 82 | ||
| @@ -96,14 +85,14 @@ private: | |||
| 96 | INSERT_PADDING_BYTES(4); | 85 | INSERT_PADDING_BYTES(4); |
| 97 | u64_le address{}; | 86 | u64_le address{}; |
| 98 | u32_le size{}; | 87 | u32_le size{}; |
| 99 | u32_le flags{}; | 88 | NvCore::NvMap::Handle::Flags flags{}; |
| 100 | }; | 89 | }; |
| 101 | static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); | 90 | static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); |
| 102 | 91 | ||
| 103 | struct IocParamParams { | 92 | struct IocParamParams { |
| 104 | // Input | 93 | // Input |
| 105 | u32_le handle{}; | 94 | u32_le handle{}; |
| 106 | u32_le param{}; | 95 | HandleParameterType param{}; |
| 107 | // Output | 96 | // Output |
| 108 | u32_le result{}; | 97 | u32_le result{}; |
| 109 | }; | 98 | }; |
| @@ -117,14 +106,15 @@ private: | |||
| 117 | }; | 106 | }; |
| 118 | static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); | 107 | static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); |
| 119 | 108 | ||
| 120 | u32 CreateObject(u32 size); | ||
| 121 | |||
| 122 | NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output); | 109 | NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output); |
| 123 | NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); | 110 | NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); |
| 124 | NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output); | 111 | NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output); |
| 125 | NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output); | 112 | NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output); |
| 126 | NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output); | 113 | NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output); |
| 127 | NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output); | 114 | NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output); |
| 115 | |||
| 116 | NvCore::Container& container; | ||
| 117 | NvCore::NvMap& file; | ||
| 128 | }; | 118 | }; |
| 129 | 119 | ||
| 130 | } // namespace Service::Nvidia::Devices | 120 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index 1d00394c8..0e2f47075 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors |
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | 4 | ||
| 4 | #pragma once | 5 | #pragma once |
| 5 | 6 | ||
| @@ -78,11 +79,15 @@ enum class NvResult : u32 { | |||
| 78 | ModuleNotPresent = 0xA000E, | 79 | ModuleNotPresent = 0xA000E, |
| 79 | }; | 80 | }; |
| 80 | 81 | ||
| 82 | // obtained from | ||
| 83 | // https://github.com/skyline-emu/skyline/blob/nvdec-dev/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/ctrl.h#L47 | ||
| 81 | enum class EventState { | 84 | enum class EventState { |
| 82 | Free = 0, | 85 | Available = 0, |
| 83 | Registered = 1, | 86 | Waiting = 1, |
| 84 | Waiting = 2, | 87 | Cancelling = 2, |
| 85 | Busy = 3, | 88 | Signalling = 3, |
| 89 | Signalled = 4, | ||
| 90 | Cancelled = 5, | ||
| 86 | }; | 91 | }; |
| 87 | 92 | ||
| 88 | union Ioctl { | 93 | union Ioctl { |
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 756eb7453..5e7b7468f 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors |
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | 4 | ||
| 4 | #include <utility> | 5 | #include <utility> |
| 5 | 6 | ||
| @@ -8,6 +9,7 @@ | |||
| 8 | #include "core/hle/ipc_helpers.h" | 9 | #include "core/hle/ipc_helpers.h" |
| 9 | #include "core/hle/kernel/k_event.h" | 10 | #include "core/hle/kernel/k_event.h" |
| 10 | #include "core/hle/kernel/k_writable_event.h" | 11 | #include "core/hle/kernel/k_writable_event.h" |
| 12 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 11 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 13 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| 12 | #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" | 14 | #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" |
| 13 | #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" | 15 | #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" |
| @@ -15,17 +17,31 @@ | |||
| 15 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" | 17 | #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" |
| 16 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" | 18 | #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" |
| 17 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" | 19 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" |
| 20 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" | ||
| 18 | #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" | 21 | #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" |
| 19 | #include "core/hle/service/nvdrv/devices/nvhost_vic.h" | 22 | #include "core/hle/service/nvdrv/devices/nvhost_vic.h" |
| 20 | #include "core/hle/service/nvdrv/devices/nvmap.h" | 23 | #include "core/hle/service/nvdrv/devices/nvmap.h" |
| 21 | #include "core/hle/service/nvdrv/nvdrv.h" | 24 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 22 | #include "core/hle/service/nvdrv/nvdrv_interface.h" | 25 | #include "core/hle/service/nvdrv/nvdrv_interface.h" |
| 23 | #include "core/hle/service/nvdrv/nvmemp.h" | 26 | #include "core/hle/service/nvdrv/nvmemp.h" |
| 24 | #include "core/hle/service/nvdrv/syncpoint_manager.h" | ||
| 25 | #include "core/hle/service/nvflinger/nvflinger.h" | 27 | #include "core/hle/service/nvflinger/nvflinger.h" |
| 28 | #include "video_core/gpu.h" | ||
| 26 | 29 | ||
| 27 | namespace Service::Nvidia { | 30 | namespace Service::Nvidia { |
| 28 | 31 | ||
| 32 | EventInterface::EventInterface(Module& module_) : module{module_}, guard{}, on_signal{} {} | ||
| 33 | |||
| 34 | EventInterface::~EventInterface() = default; | ||
| 35 | |||
| 36 | Kernel::KEvent* EventInterface::CreateEvent(std::string name) { | ||
| 37 | Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name)); | ||
| 38 | return new_event; | ||
| 39 | } | ||
| 40 | |||
| 41 | void EventInterface::FreeEvent(Kernel::KEvent* event) { | ||
| 42 | module.service_context.CloseEvent(event); | ||
| 43 | } | ||
| 44 | |||
| 29 | void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, | 45 | void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, |
| 30 | Core::System& system) { | 46 | Core::System& system) { |
| 31 | auto module_ = std::make_shared<Module>(system); | 47 | auto module_ = std::make_shared<Module>(system); |
| @@ -38,34 +54,54 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger | |||
| 38 | } | 54 | } |
| 39 | 55 | ||
| 40 | Module::Module(Core::System& system) | 56 | Module::Module(Core::System& system) |
| 41 | : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} { | 57 | : service_context{system, "nvdrv"}, events_interface{*this}, container{system.Host1x()} { |
| 42 | for (u32 i = 0; i < MaxNvEvents; i++) { | 58 | builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { |
| 43 | events_interface.events[i].event = | 59 | std::shared_ptr<Devices::nvdevice> device = |
| 44 | service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); | 60 | std::make_shared<Devices::nvhost_as_gpu>(system, *this, container); |
| 45 | events_interface.status[i] = EventState::Free; | 61 | return open_files.emplace(fd, device).first; |
| 46 | events_interface.registered[i] = false; | 62 | }; |
| 47 | } | 63 | builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) { |
| 48 | auto nvmap_dev = std::make_shared<Devices::nvmap>(system); | 64 | std::shared_ptr<Devices::nvdevice> device = |
| 49 | devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev); | 65 | std::make_shared<Devices::nvhost_gpu>(system, events_interface, container); |
| 50 | devices["/dev/nvhost-gpu"] = | 66 | return open_files.emplace(fd, device).first; |
| 51 | std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager); | 67 | }; |
| 52 | devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system); | 68 | builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) { |
| 53 | devices["/dev/nvmap"] = nvmap_dev; | 69 | std::shared_ptr<Devices::nvdevice> device = |
| 54 | devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); | 70 | std::make_shared<Devices::nvhost_ctrl_gpu>(system, events_interface); |
| 55 | devices["/dev/nvhost-ctrl"] = | 71 | return open_files.emplace(fd, device).first; |
| 56 | std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager); | 72 | }; |
| 57 | devices["/dev/nvhost-nvdec"] = | 73 | builders["/dev/nvmap"] = [this, &system](DeviceFD fd) { |
| 58 | std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev, syncpoint_manager); | 74 | std::shared_ptr<Devices::nvdevice> device = |
| 59 | devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); | 75 | std::make_shared<Devices::nvmap>(system, container); |
| 60 | devices["/dev/nvhost-vic"] = | 76 | return open_files.emplace(fd, device).first; |
| 61 | std::make_shared<Devices::nvhost_vic>(system, nvmap_dev, syncpoint_manager); | 77 | }; |
| 78 | builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) { | ||
| 79 | std::shared_ptr<Devices::nvdevice> device = | ||
| 80 | std::make_shared<Devices::nvdisp_disp0>(system, container); | ||
| 81 | return open_files.emplace(fd, device).first; | ||
| 82 | }; | ||
| 83 | builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) { | ||
| 84 | std::shared_ptr<Devices::nvdevice> device = | ||
| 85 | std::make_shared<Devices::nvhost_ctrl>(system, events_interface, container); | ||
| 86 | return open_files.emplace(fd, device).first; | ||
| 87 | }; | ||
| 88 | builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) { | ||
| 89 | std::shared_ptr<Devices::nvdevice> device = | ||
| 90 | std::make_shared<Devices::nvhost_nvdec>(system, container); | ||
| 91 | return open_files.emplace(fd, device).first; | ||
| 92 | }; | ||
| 93 | builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) { | ||
| 94 | std::shared_ptr<Devices::nvdevice> device = std::make_shared<Devices::nvhost_nvjpg>(system); | ||
| 95 | return open_files.emplace(fd, device).first; | ||
| 96 | }; | ||
| 97 | builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) { | ||
| 98 | std::shared_ptr<Devices::nvdevice> device = | ||
| 99 | std::make_shared<Devices::nvhost_vic>(system, container); | ||
| 100 | return open_files.emplace(fd, device).first; | ||
| 101 | }; | ||
| 62 | } | 102 | } |
| 63 | 103 | ||
| 64 | Module::~Module() { | 104 | Module::~Module() {} |
| 65 | for (u32 i = 0; i < MaxNvEvents; i++) { | ||
| 66 | service_context.CloseEvent(events_interface.events[i].event); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | 105 | ||
| 70 | NvResult Module::VerifyFD(DeviceFD fd) const { | 106 | NvResult Module::VerifyFD(DeviceFD fd) const { |
| 71 | if (fd < 0) { | 107 | if (fd < 0) { |
| @@ -82,18 +118,18 @@ NvResult Module::VerifyFD(DeviceFD fd) const { | |||
| 82 | } | 118 | } |
| 83 | 119 | ||
| 84 | DeviceFD Module::Open(const std::string& device_name) { | 120 | DeviceFD Module::Open(const std::string& device_name) { |
| 85 | if (devices.find(device_name) == devices.end()) { | 121 | auto it = builders.find(device_name); |
| 122 | if (it == builders.end()) { | ||
| 86 | LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); | 123 | LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); |
| 87 | return INVALID_NVDRV_FD; | 124 | return INVALID_NVDRV_FD; |
| 88 | } | 125 | } |
| 89 | 126 | ||
| 90 | auto device = devices[device_name]; | ||
| 91 | const DeviceFD fd = next_fd++; | 127 | const DeviceFD fd = next_fd++; |
| 128 | auto& builder = it->second; | ||
| 129 | auto device = builder(fd)->second; | ||
| 92 | 130 | ||
| 93 | device->OnOpen(fd); | 131 | device->OnOpen(fd); |
| 94 | 132 | ||
| 95 | open_files[fd] = std::move(device); | ||
| 96 | |||
| 97 | return fd; | 133 | return fd; |
| 98 | } | 134 | } |
| 99 | 135 | ||
| @@ -168,22 +204,24 @@ NvResult Module::Close(DeviceFD fd) { | |||
| 168 | return NvResult::Success; | 204 | return NvResult::Success; |
| 169 | } | 205 | } |
| 170 | 206 | ||
| 171 | void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { | 207 | NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) { |
| 172 | for (u32 i = 0; i < MaxNvEvents; i++) { | 208 | if (fd < 0) { |
| 173 | if (events_interface.assigned_syncpt[i] == syncpoint_id && | 209 | LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); |
| 174 | events_interface.assigned_value[i] == value) { | 210 | return NvResult::InvalidState; |
| 175 | events_interface.LiberateEvent(i); | ||
| 176 | events_interface.events[i].event->GetWritableEvent().Signal(); | ||
| 177 | } | ||
| 178 | } | 211 | } |
| 179 | } | ||
| 180 | 212 | ||
| 181 | Kernel::KReadableEvent& Module::GetEvent(const u32 event_id) { | 213 | const auto itr = open_files.find(fd); |
| 182 | return events_interface.events[event_id].event->GetReadableEvent(); | ||
| 183 | } | ||
| 184 | 214 | ||
| 185 | Kernel::KWritableEvent& Module::GetEventWriteable(const u32 event_id) { | 215 | if (itr == open_files.end()) { |
| 186 | return events_interface.events[event_id].event->GetWritableEvent(); | 216 | LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); |
| 217 | return NvResult::NotImplemented; | ||
| 218 | } | ||
| 219 | |||
| 220 | event = itr->second->QueryEvent(event_id); | ||
| 221 | if (!event) { | ||
| 222 | return NvResult::BadParameter; | ||
| 223 | } | ||
| 224 | return NvResult::Success; | ||
| 187 | } | 225 | } |
| 188 | 226 | ||
| 189 | } // namespace Service::Nvidia | 227 | } // namespace Service::Nvidia |
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index c929e5106..146d046a9 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h | |||
| @@ -1,16 +1,20 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors |
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | 4 | ||
| 4 | #pragma once | 5 | #pragma once |
| 5 | 6 | ||
| 7 | #include <functional> | ||
| 8 | #include <list> | ||
| 6 | #include <memory> | 9 | #include <memory> |
| 10 | #include <string> | ||
| 7 | #include <unordered_map> | 11 | #include <unordered_map> |
| 8 | #include <vector> | 12 | #include <vector> |
| 9 | 13 | ||
| 10 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 11 | #include "core/hle/service/kernel_helpers.h" | 15 | #include "core/hle/service/kernel_helpers.h" |
| 16 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 12 | #include "core/hle/service/nvdrv/nvdata.h" | 17 | #include "core/hle/service/nvdrv/nvdata.h" |
| 13 | #include "core/hle/service/nvdrv/syncpoint_manager.h" | ||
| 14 | #include "core/hle/service/nvflinger/ui/fence.h" | 18 | #include "core/hle/service/nvflinger/ui/fence.h" |
| 15 | #include "core/hle/service/service.h" | 19 | #include "core/hle/service/service.h" |
| 16 | 20 | ||
| @@ -28,81 +32,31 @@ class NVFlinger; | |||
| 28 | 32 | ||
| 29 | namespace Service::Nvidia { | 33 | namespace Service::Nvidia { |
| 30 | 34 | ||
| 35 | namespace NvCore { | ||
| 36 | class Container; | ||
| 31 | class SyncpointManager; | 37 | class SyncpointManager; |
| 38 | } // namespace NvCore | ||
| 32 | 39 | ||
| 33 | namespace Devices { | 40 | namespace Devices { |
| 34 | class nvdevice; | 41 | class nvdevice; |
| 35 | } | 42 | class nvhost_ctrl; |
| 43 | } // namespace Devices | ||
| 36 | 44 | ||
| 37 | /// Represents an Nvidia event | 45 | class Module; |
| 38 | struct NvEvent { | ||
| 39 | Kernel::KEvent* event{}; | ||
| 40 | NvFence fence{}; | ||
| 41 | }; | ||
| 42 | 46 | ||
| 43 | struct EventInterface { | 47 | class EventInterface { |
| 44 | // Mask representing currently busy events | 48 | public: |
| 45 | u64 events_mask{}; | 49 | explicit EventInterface(Module& module_); |
| 46 | // Each kernel event associated to an NV event | 50 | ~EventInterface(); |
| 47 | std::array<NvEvent, MaxNvEvents> events; | 51 | |
| 48 | // The status of the current NVEvent | 52 | Kernel::KEvent* CreateEvent(std::string name); |
| 49 | std::array<EventState, MaxNvEvents> status{}; | 53 | |
| 50 | // Tells if an NVEvent is registered or not | 54 | void FreeEvent(Kernel::KEvent* event); |
| 51 | std::array<bool, MaxNvEvents> registered{}; | 55 | |
| 52 | // Tells the NVEvent that it has failed. | 56 | private: |
| 53 | std::array<bool, MaxNvEvents> failed{}; | 57 | Module& module; |
| 54 | // When an NVEvent is waiting on GPU interrupt, this is the sync_point | 58 | std::mutex guard; |
| 55 | // associated with it. | 59 | std::list<Devices::nvhost_ctrl*> on_signal; |
| 56 | std::array<u32, MaxNvEvents> assigned_syncpt{}; | ||
| 57 | // This is the value of the GPU interrupt for which the NVEvent is waiting | ||
| 58 | // for. | ||
| 59 | std::array<u32, MaxNvEvents> assigned_value{}; | ||
| 60 | // Constant to denote an unasigned syncpoint. | ||
| 61 | static constexpr u32 unassigned_syncpt = 0xFFFFFFFF; | ||
| 62 | std::optional<u32> GetFreeEvent() const { | ||
| 63 | u64 mask = events_mask; | ||
| 64 | for (u32 i = 0; i < MaxNvEvents; i++) { | ||
| 65 | const bool is_free = (mask & 0x1) == 0; | ||
| 66 | if (is_free) { | ||
| 67 | if (status[i] == EventState::Registered || status[i] == EventState::Free) { | ||
| 68 | return {i}; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | mask = mask >> 1; | ||
| 72 | } | ||
| 73 | return std::nullopt; | ||
| 74 | } | ||
| 75 | void SetEventStatus(const u32 event_id, EventState new_status) { | ||
| 76 | EventState old_status = status[event_id]; | ||
| 77 | if (old_status == new_status) { | ||
| 78 | return; | ||
| 79 | } | ||
| 80 | status[event_id] = new_status; | ||
| 81 | if (new_status == EventState::Registered) { | ||
| 82 | registered[event_id] = true; | ||
| 83 | } | ||
| 84 | if (new_status == EventState::Waiting || new_status == EventState::Busy) { | ||
| 85 | events_mask |= (1ULL << event_id); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | void RegisterEvent(const u32 event_id) { | ||
| 89 | registered[event_id] = true; | ||
| 90 | if (status[event_id] == EventState::Free) { | ||
| 91 | status[event_id] = EventState::Registered; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | void UnregisterEvent(const u32 event_id) { | ||
| 95 | registered[event_id] = false; | ||
| 96 | if (status[event_id] == EventState::Registered) { | ||
| 97 | status[event_id] = EventState::Free; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | void LiberateEvent(const u32 event_id) { | ||
| 101 | status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free; | ||
| 102 | events_mask &= ~(1ULL << event_id); | ||
| 103 | assigned_syncpt[event_id] = unassigned_syncpt; | ||
| 104 | assigned_value[event_id] = 0; | ||
| 105 | } | ||
| 106 | }; | 60 | }; |
| 107 | 61 | ||
| 108 | class Module final { | 62 | class Module final { |
| @@ -112,9 +66,9 @@ public: | |||
| 112 | 66 | ||
| 113 | /// Returns a pointer to one of the available devices, identified by its name. | 67 | /// Returns a pointer to one of the available devices, identified by its name. |
| 114 | template <typename T> | 68 | template <typename T> |
| 115 | std::shared_ptr<T> GetDevice(const std::string& name) { | 69 | std::shared_ptr<T> GetDevice(DeviceFD fd) { |
| 116 | auto itr = devices.find(name); | 70 | auto itr = open_files.find(fd); |
| 117 | if (itr == devices.end()) | 71 | if (itr == open_files.end()) |
| 118 | return nullptr; | 72 | return nullptr; |
| 119 | return std::static_pointer_cast<T>(itr->second); | 73 | return std::static_pointer_cast<T>(itr->second); |
| 120 | } | 74 | } |
| @@ -137,28 +91,27 @@ public: | |||
| 137 | /// Closes a device file descriptor and returns operation success. | 91 | /// Closes a device file descriptor and returns operation success. |
| 138 | NvResult Close(DeviceFD fd); | 92 | NvResult Close(DeviceFD fd); |
| 139 | 93 | ||
| 140 | void SignalSyncpt(const u32 syncpoint_id, const u32 value); | 94 | NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event); |
| 141 | |||
| 142 | Kernel::KReadableEvent& GetEvent(u32 event_id); | ||
| 143 | |||
| 144 | Kernel::KWritableEvent& GetEventWriteable(u32 event_id); | ||
| 145 | 95 | ||
| 146 | private: | 96 | private: |
| 147 | /// Manages syncpoints on the host | 97 | friend class EventInterface; |
| 148 | SyncpointManager syncpoint_manager; | 98 | friend class Service::NVFlinger::NVFlinger; |
| 149 | 99 | ||
| 150 | /// Id to use for the next open file descriptor. | 100 | /// Id to use for the next open file descriptor. |
| 151 | DeviceFD next_fd = 1; | 101 | DeviceFD next_fd = 1; |
| 152 | 102 | ||
| 103 | using FilesContainerType = std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>>; | ||
| 153 | /// Mapping of file descriptors to the devices they reference. | 104 | /// Mapping of file descriptors to the devices they reference. |
| 154 | std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files; | 105 | FilesContainerType open_files; |
| 155 | 106 | ||
| 156 | /// Mapping of device node names to their implementation. | 107 | KernelHelpers::ServiceContext service_context; |
| 157 | std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; | ||
| 158 | 108 | ||
| 159 | EventInterface events_interface; | 109 | EventInterface events_interface; |
| 160 | 110 | ||
| 161 | KernelHelpers::ServiceContext service_context; | 111 | /// Manages syncpoints on the host |
| 112 | NvCore::Container container; | ||
| 113 | |||
| 114 | std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders; | ||
| 162 | }; | 115 | }; |
| 163 | 116 | ||
| 164 | /// Registers all NVDRV services with the specified service manager. | 117 | /// Registers all NVDRV services with the specified service manager. |
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index b5a980384..edbdfee43 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp | |||
| @@ -1,10 +1,12 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors |
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | 4 | ||
| 4 | #include <cinttypes> | 5 | #include <cinttypes> |
| 5 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 6 | #include "core/core.h" | 7 | #include "core/core.h" |
| 7 | #include "core/hle/ipc_helpers.h" | 8 | #include "core/hle/ipc_helpers.h" |
| 9 | #include "core/hle/kernel/k_event.h" | ||
| 8 | #include "core/hle/kernel/k_readable_event.h" | 10 | #include "core/hle/kernel/k_readable_event.h" |
| 9 | #include "core/hle/service/nvdrv/nvdata.h" | 11 | #include "core/hle/service/nvdrv/nvdata.h" |
| 10 | #include "core/hle/service/nvdrv/nvdrv.h" | 12 | #include "core/hle/service/nvdrv/nvdrv.h" |
| @@ -12,10 +14,6 @@ | |||
| 12 | 14 | ||
| 13 | namespace Service::Nvidia { | 15 | namespace Service::Nvidia { |
| 14 | 16 | ||
| 15 | void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { | ||
| 16 | nvdrv->SignalSyncpt(syncpoint_id, value); | ||
| 17 | } | ||
| 18 | |||
| 19 | void NVDRV::Open(Kernel::HLERequestContext& ctx) { | 17 | void NVDRV::Open(Kernel::HLERequestContext& ctx) { |
| 20 | LOG_DEBUG(Service_NVDRV, "called"); | 18 | LOG_DEBUG(Service_NVDRV, "called"); |
| 21 | IPC::ResponseBuilder rb{ctx, 4}; | 19 | IPC::ResponseBuilder rb{ctx, 4}; |
| @@ -164,8 +162,7 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { | |||
| 164 | void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { | 162 | void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { |
| 165 | IPC::RequestParser rp{ctx}; | 163 | IPC::RequestParser rp{ctx}; |
| 166 | const auto fd = rp.Pop<DeviceFD>(); | 164 | const auto fd = rp.Pop<DeviceFD>(); |
| 167 | const auto event_id = rp.Pop<u32>() & 0x00FF; | 165 | const auto event_id = rp.Pop<u32>(); |
| 168 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); | ||
| 169 | 166 | ||
| 170 | if (!is_initialized) { | 167 | if (!is_initialized) { |
| 171 | ServiceError(ctx, NvResult::NotInitialized); | 168 | ServiceError(ctx, NvResult::NotInitialized); |
| @@ -173,24 +170,20 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { | |||
| 173 | return; | 170 | return; |
| 174 | } | 171 | } |
| 175 | 172 | ||
| 176 | const auto nv_result = nvdrv->VerifyFD(fd); | 173 | Kernel::KEvent* event = nullptr; |
| 177 | if (nv_result != NvResult::Success) { | 174 | NvResult result = nvdrv->QueryEvent(fd, event_id, event); |
| 178 | LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd); | ||
| 179 | ServiceError(ctx, nv_result); | ||
| 180 | return; | ||
| 181 | } | ||
| 182 | 175 | ||
| 183 | if (event_id < MaxNvEvents) { | 176 | if (result == NvResult::Success) { |
| 184 | IPC::ResponseBuilder rb{ctx, 3, 1}; | 177 | IPC::ResponseBuilder rb{ctx, 3, 1}; |
| 185 | rb.Push(ResultSuccess); | 178 | rb.Push(ResultSuccess); |
| 186 | auto& event = nvdrv->GetEvent(event_id); | 179 | auto& readable_event = event->GetReadableEvent(); |
| 187 | event.Clear(); | 180 | rb.PushCopyObjects(readable_event); |
| 188 | rb.PushCopyObjects(event); | ||
| 189 | rb.PushEnum(NvResult::Success); | 181 | rb.PushEnum(NvResult::Success); |
| 190 | } else { | 182 | } else { |
| 183 | LOG_ERROR(Service_NVDRV, "Invalid event request!"); | ||
| 191 | IPC::ResponseBuilder rb{ctx, 3}; | 184 | IPC::ResponseBuilder rb{ctx, 3}; |
| 192 | rb.Push(ResultSuccess); | 185 | rb.Push(ResultSuccess); |
| 193 | rb.PushEnum(NvResult::BadParameter); | 186 | rb.PushEnum(result); |
| 194 | } | 187 | } |
| 195 | } | 188 | } |
| 196 | 189 | ||
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index cbd37b52b..cd58a4f35 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h | |||
| @@ -18,8 +18,6 @@ public: | |||
| 18 | explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name); | 18 | explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name); |
| 19 | ~NVDRV() override; | 19 | ~NVDRV() override; |
| 20 | 20 | ||
| 21 | void SignalGPUInterruptSyncpt(u32 syncpoint_id, u32 value); | ||
| 22 | |||
| 23 | private: | 21 | private: |
| 24 | void Open(Kernel::HLERequestContext& ctx); | 22 | void Open(Kernel::HLERequestContext& ctx); |
| 25 | void Ioctl1(Kernel::HLERequestContext& ctx); | 23 | void Ioctl1(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp deleted file mode 100644 index a6fa943e8..000000000 --- a/src/core/hle/service/nvdrv/syncpoint_manager.cpp +++ /dev/null | |||
| @@ -1,38 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/assert.h" | ||
| 5 | #include "core/hle/service/nvdrv/syncpoint_manager.h" | ||
| 6 | #include "video_core/gpu.h" | ||
| 7 | |||
| 8 | namespace Service::Nvidia { | ||
| 9 | |||
| 10 | SyncpointManager::SyncpointManager(Tegra::GPU& gpu_) : gpu{gpu_} {} | ||
| 11 | |||
| 12 | SyncpointManager::~SyncpointManager() = default; | ||
| 13 | |||
| 14 | u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) { | ||
| 15 | syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id); | ||
| 16 | return GetSyncpointMin(syncpoint_id); | ||
| 17 | } | ||
| 18 | |||
| 19 | u32 SyncpointManager::AllocateSyncpoint() { | ||
| 20 | for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) { | ||
| 21 | if (!syncpoints[syncpoint_id].is_allocated) { | ||
| 22 | syncpoints[syncpoint_id].is_allocated = true; | ||
| 23 | return syncpoint_id; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | ASSERT_MSG(false, "No more available syncpoints!"); | ||
| 27 | return {}; | ||
| 28 | } | ||
| 29 | |||
| 30 | u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) { | ||
| 31 | for (u32 index = 0; index < value; ++index) { | ||
| 32 | syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed); | ||
| 33 | } | ||
| 34 | |||
| 35 | return GetSyncpointMax(syncpoint_id); | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace Service::Nvidia | ||
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/syncpoint_manager.h deleted file mode 100644 index 7f080f76e..000000000 --- a/src/core/hle/service/nvdrv/syncpoint_manager.h +++ /dev/null | |||
| @@ -1,84 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <atomic> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/hle/service/nvdrv/nvdata.h" | ||
| 11 | |||
| 12 | namespace Tegra { | ||
| 13 | class GPU; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Service::Nvidia { | ||
| 17 | |||
| 18 | class SyncpointManager final { | ||
| 19 | public: | ||
| 20 | explicit SyncpointManager(Tegra::GPU& gpu_); | ||
| 21 | ~SyncpointManager(); | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Returns true if the specified syncpoint is expired for the given value. | ||
| 25 | * @param syncpoint_id Syncpoint ID to check. | ||
| 26 | * @param value Value to check against the specified syncpoint. | ||
| 27 | * @returns True if the specified syncpoint is expired for the given value, otherwise False. | ||
| 28 | */ | ||
| 29 | bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const { | ||
| 30 | return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value); | ||
| 31 | } | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Gets the lower bound for the specified syncpoint. | ||
| 35 | * @param syncpoint_id Syncpoint ID to get the lower bound for. | ||
| 36 | * @returns The lower bound for the specified syncpoint. | ||
| 37 | */ | ||
| 38 | u32 GetSyncpointMin(u32 syncpoint_id) const { | ||
| 39 | return syncpoints.at(syncpoint_id).min.load(std::memory_order_relaxed); | ||
| 40 | } | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Gets the uper bound for the specified syncpoint. | ||
| 44 | * @param syncpoint_id Syncpoint ID to get the upper bound for. | ||
| 45 | * @returns The upper bound for the specified syncpoint. | ||
| 46 | */ | ||
| 47 | u32 GetSyncpointMax(u32 syncpoint_id) const { | ||
| 48 | return syncpoints.at(syncpoint_id).max.load(std::memory_order_relaxed); | ||
| 49 | } | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Refreshes the minimum value for the specified syncpoint. | ||
| 53 | * @param syncpoint_id Syncpoint ID to be refreshed. | ||
| 54 | * @returns The new syncpoint minimum value. | ||
| 55 | */ | ||
| 56 | u32 RefreshSyncpoint(u32 syncpoint_id); | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Allocates a new syncoint. | ||
| 60 | * @returns The syncpoint ID for the newly allocated syncpoint. | ||
| 61 | */ | ||
| 62 | u32 AllocateSyncpoint(); | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Increases the maximum value for the specified syncpoint. | ||
| 66 | * @param syncpoint_id Syncpoint ID to be increased. | ||
| 67 | * @param value Value to increase the specified syncpoint by. | ||
| 68 | * @returns The new syncpoint maximum value. | ||
| 69 | */ | ||
| 70 | u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value); | ||
| 71 | |||
| 72 | private: | ||
| 73 | struct Syncpoint { | ||
| 74 | std::atomic<u32> min; | ||
| 75 | std::atomic<u32> max; | ||
| 76 | std::atomic<bool> is_allocated; | ||
| 77 | }; | ||
| 78 | |||
| 79 | std::array<Syncpoint, MaxSyncPoints> syncpoints{}; | ||
| 80 | |||
| 81 | Tegra::GPU& gpu; | ||
| 82 | }; | ||
| 83 | |||
| 84 | } // namespace Service::Nvidia | ||
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp index 4b3d5efd6..1ce67c771 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp | |||
| @@ -5,15 +5,18 @@ | |||
| 5 | // https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp | 5 | // https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp |
| 6 | 6 | ||
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/hle/service/nvdrv/core/nvmap.h" | ||
| 8 | #include "core/hle/service/nvflinger/buffer_item.h" | 9 | #include "core/hle/service/nvflinger/buffer_item.h" |
| 9 | #include "core/hle/service/nvflinger/buffer_queue_consumer.h" | 10 | #include "core/hle/service/nvflinger/buffer_queue_consumer.h" |
| 10 | #include "core/hle/service/nvflinger/buffer_queue_core.h" | 11 | #include "core/hle/service/nvflinger/buffer_queue_core.h" |
| 11 | #include "core/hle/service/nvflinger/producer_listener.h" | 12 | #include "core/hle/service/nvflinger/producer_listener.h" |
| 13 | #include "core/hle/service/nvflinger/ui/graphic_buffer.h" | ||
| 12 | 14 | ||
| 13 | namespace Service::android { | 15 | namespace Service::android { |
| 14 | 16 | ||
| 15 | BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_) | 17 | BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, |
| 16 | : core{std::move(core_)}, slots{core->slots} {} | 18 | Service::Nvidia::NvCore::NvMap& nvmap_) |
| 19 | : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {} | ||
| 17 | 20 | ||
| 18 | BufferQueueConsumer::~BufferQueueConsumer() = default; | 21 | BufferQueueConsumer::~BufferQueueConsumer() = default; |
| 19 | 22 | ||
| @@ -133,6 +136,8 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc | |||
| 133 | 136 | ||
| 134 | slots[slot].buffer_state = BufferState::Free; | 137 | slots[slot].buffer_state = BufferState::Free; |
| 135 | 138 | ||
| 139 | nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true); | ||
| 140 | |||
| 136 | listener = core->connected_producer_listener; | 141 | listener = core->connected_producer_listener; |
| 137 | 142 | ||
| 138 | LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); | 143 | LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); |
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h index b598c314f..4ec06ca13 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h | |||
| @@ -13,6 +13,10 @@ | |||
| 13 | #include "core/hle/service/nvflinger/buffer_queue_defs.h" | 13 | #include "core/hle/service/nvflinger/buffer_queue_defs.h" |
| 14 | #include "core/hle/service/nvflinger/status.h" | 14 | #include "core/hle/service/nvflinger/status.h" |
| 15 | 15 | ||
| 16 | namespace Service::Nvidia::NvCore { | ||
| 17 | class NvMap; | ||
| 18 | } // namespace Service::Nvidia::NvCore | ||
| 19 | |||
| 16 | namespace Service::android { | 20 | namespace Service::android { |
| 17 | 21 | ||
| 18 | class BufferItem; | 22 | class BufferItem; |
| @@ -21,7 +25,8 @@ class IConsumerListener; | |||
| 21 | 25 | ||
| 22 | class BufferQueueConsumer final { | 26 | class BufferQueueConsumer final { |
| 23 | public: | 27 | public: |
| 24 | explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_); | 28 | explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, |
| 29 | Service::Nvidia::NvCore::NvMap& nvmap_); | ||
| 25 | ~BufferQueueConsumer(); | 30 | ~BufferQueueConsumer(); |
| 26 | 31 | ||
| 27 | Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); | 32 | Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); |
| @@ -32,6 +37,7 @@ public: | |||
| 32 | private: | 37 | private: |
| 33 | std::shared_ptr<BufferQueueCore> core; | 38 | std::shared_ptr<BufferQueueCore> core; |
| 34 | BufferQueueDefs::SlotsType& slots; | 39 | BufferQueueDefs::SlotsType& slots; |
| 40 | Service::Nvidia::NvCore::NvMap& nvmap; | ||
| 35 | }; | 41 | }; |
| 36 | 42 | ||
| 37 | } // namespace Service::android | 43 | } // namespace Service::android |
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index 337431488..d4ab23a10 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | #include "core/hle/kernel/k_writable_event.h" | 14 | #include "core/hle/kernel/k_writable_event.h" |
| 15 | #include "core/hle/kernel/kernel.h" | 15 | #include "core/hle/kernel/kernel.h" |
| 16 | #include "core/hle/service/kernel_helpers.h" | 16 | #include "core/hle/service/kernel_helpers.h" |
| 17 | #include "core/hle/service/nvdrv/nvdrv.h" | 17 | #include "core/hle/service/nvdrv/core/nvmap.h" |
| 18 | #include "core/hle/service/nvflinger/buffer_queue_core.h" | 18 | #include "core/hle/service/nvflinger/buffer_queue_core.h" |
| 19 | #include "core/hle/service/nvflinger/buffer_queue_producer.h" | 19 | #include "core/hle/service/nvflinger/buffer_queue_producer.h" |
| 20 | #include "core/hle/service/nvflinger/consumer_listener.h" | 20 | #include "core/hle/service/nvflinger/consumer_listener.h" |
| @@ -26,8 +26,10 @@ | |||
| 26 | namespace Service::android { | 26 | namespace Service::android { |
| 27 | 27 | ||
| 28 | BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, | 28 | BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, |
| 29 | std::shared_ptr<BufferQueueCore> buffer_queue_core_) | 29 | std::shared_ptr<BufferQueueCore> buffer_queue_core_, |
| 30 | : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) { | 30 | Service::Nvidia::NvCore::NvMap& nvmap_) |
| 31 | : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots), | ||
| 32 | nvmap(nvmap_) { | ||
| 31 | buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); | 33 | buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); |
| 32 | } | 34 | } |
| 33 | 35 | ||
| @@ -530,6 +532,8 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, | |||
| 530 | item.is_droppable = core->dequeue_buffer_cannot_block || async; | 532 | item.is_droppable = core->dequeue_buffer_cannot_block || async; |
| 531 | item.swap_interval = swap_interval; | 533 | item.swap_interval = swap_interval; |
| 532 | 534 | ||
| 535 | nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true); | ||
| 536 | |||
| 533 | sticky_transform = sticky_transform_; | 537 | sticky_transform = sticky_transform_; |
| 534 | 538 | ||
| 535 | if (core->queue.empty()) { | 539 | if (core->queue.empty()) { |
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h index 42d4722dc..0ba03a568 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h | |||
| @@ -31,6 +31,10 @@ namespace Service::KernelHelpers { | |||
| 31 | class ServiceContext; | 31 | class ServiceContext; |
| 32 | } // namespace Service::KernelHelpers | 32 | } // namespace Service::KernelHelpers |
| 33 | 33 | ||
| 34 | namespace Service::Nvidia::NvCore { | ||
| 35 | class NvMap; | ||
| 36 | } // namespace Service::Nvidia::NvCore | ||
| 37 | |||
| 34 | namespace Service::android { | 38 | namespace Service::android { |
| 35 | 39 | ||
| 36 | class BufferQueueCore; | 40 | class BufferQueueCore; |
| @@ -39,7 +43,8 @@ class IProducerListener; | |||
| 39 | class BufferQueueProducer final : public IBinder { | 43 | class BufferQueueProducer final : public IBinder { |
| 40 | public: | 44 | public: |
| 41 | explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, | 45 | explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, |
| 42 | std::shared_ptr<BufferQueueCore> buffer_queue_core_); | 46 | std::shared_ptr<BufferQueueCore> buffer_queue_core_, |
| 47 | Service::Nvidia::NvCore::NvMap& nvmap_); | ||
| 43 | ~BufferQueueProducer(); | 48 | ~BufferQueueProducer(); |
| 44 | 49 | ||
| 45 | void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override; | 50 | void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override; |
| @@ -78,6 +83,8 @@ private: | |||
| 78 | s32 next_callback_ticket{}; | 83 | s32 next_callback_ticket{}; |
| 79 | s32 current_callback_ticket{}; | 84 | s32 current_callback_ticket{}; |
| 80 | std::condition_variable_any callback_condition; | 85 | std::condition_variable_any callback_condition; |
| 86 | |||
| 87 | Service::Nvidia::NvCore::NvMap& nvmap; | ||
| 81 | }; | 88 | }; |
| 82 | 89 | ||
| 83 | } // namespace Service::android | 90 | } // namespace Service::android |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 9b382bf56..aa14d2cbc 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -22,7 +22,10 @@ | |||
| 22 | #include "core/hle/service/nvflinger/ui/graphic_buffer.h" | 22 | #include "core/hle/service/nvflinger/ui/graphic_buffer.h" |
| 23 | #include "core/hle/service/vi/display/vi_display.h" | 23 | #include "core/hle/service/vi/display/vi_display.h" |
| 24 | #include "core/hle/service/vi/layer/vi_layer.h" | 24 | #include "core/hle/service/vi/layer/vi_layer.h" |
| 25 | #include "core/hle/service/vi/vi_results.h" | ||
| 25 | #include "video_core/gpu.h" | 26 | #include "video_core/gpu.h" |
| 27 | #include "video_core/host1x/host1x.h" | ||
| 28 | #include "video_core/host1x/syncpoint_manager.h" | ||
| 26 | 29 | ||
| 27 | namespace Service::NVFlinger { | 30 | namespace Service::NVFlinger { |
| 28 | 31 | ||
| @@ -30,7 +33,7 @@ constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60}; | |||
| 30 | 33 | ||
| 31 | void NVFlinger::SplitVSync(std::stop_token stop_token) { | 34 | void NVFlinger::SplitVSync(std::stop_token stop_token) { |
| 32 | system.RegisterHostThread(); | 35 | system.RegisterHostThread(); |
| 33 | std::string name = "yuzu:VSyncThread"; | 36 | std::string name = "VSyncThread"; |
| 34 | MicroProfileOnThreadCreate(name.c_str()); | 37 | MicroProfileOnThreadCreate(name.c_str()); |
| 35 | 38 | ||
| 36 | // Cleanup | 39 | // Cleanup |
| @@ -104,10 +107,15 @@ NVFlinger::~NVFlinger() { | |||
| 104 | display.GetLayer(layer).Core().NotifyShutdown(); | 107 | display.GetLayer(layer).Core().NotifyShutdown(); |
| 105 | } | 108 | } |
| 106 | } | 109 | } |
| 110 | |||
| 111 | if (nvdrv) { | ||
| 112 | nvdrv->Close(disp_fd); | ||
| 113 | } | ||
| 107 | } | 114 | } |
| 108 | 115 | ||
| 109 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { | 116 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { |
| 110 | nvdrv = std::move(instance); | 117 | nvdrv = std::move(instance); |
| 118 | disp_fd = nvdrv->Open("/dev/nvdisp_disp0"); | ||
| 111 | } | 119 | } |
| 112 | 120 | ||
| 113 | std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { | 121 | std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { |
| @@ -141,7 +149,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { | |||
| 141 | 149 | ||
| 142 | void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { | 150 | void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { |
| 143 | const auto buffer_id = next_buffer_queue_id++; | 151 | const auto buffer_id = next_buffer_queue_id++; |
| 144 | display.CreateLayer(layer_id, buffer_id); | 152 | display.CreateLayer(layer_id, buffer_id, nvdrv->container); |
| 145 | } | 153 | } |
| 146 | 154 | ||
| 147 | void NVFlinger::CloseLayer(u64 layer_id) { | 155 | void NVFlinger::CloseLayer(u64 layer_id) { |
| @@ -163,15 +171,15 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) { | |||
| 163 | return layer->GetBinderId(); | 171 | return layer->GetBinderId(); |
| 164 | } | 172 | } |
| 165 | 173 | ||
| 166 | Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { | 174 | ResultVal<Kernel::KReadableEvent*> NVFlinger::FindVsyncEvent(u64 display_id) { |
| 167 | const auto lock_guard = Lock(); | 175 | const auto lock_guard = Lock(); |
| 168 | auto* const display = FindDisplay(display_id); | 176 | auto* const display = FindDisplay(display_id); |
| 169 | 177 | ||
| 170 | if (display == nullptr) { | 178 | if (display == nullptr) { |
| 171 | return nullptr; | 179 | return VI::ResultNotFound; |
| 172 | } | 180 | } |
| 173 | 181 | ||
| 174 | return &display->GetVSyncEvent(); | 182 | return display->GetVSyncEvent(); |
| 175 | } | 183 | } |
| 176 | 184 | ||
| 177 | VI::Display* NVFlinger::FindDisplay(u64 display_id) { | 185 | VI::Display* NVFlinger::FindDisplay(u64 display_id) { |
| @@ -261,30 +269,24 @@ void NVFlinger::Compose() { | |||
| 261 | return; // We are likely shutting down | 269 | return; // We are likely shutting down |
| 262 | } | 270 | } |
| 263 | 271 | ||
| 264 | auto& gpu = system.GPU(); | ||
| 265 | const auto& multi_fence = buffer.fence; | ||
| 266 | guard->unlock(); | ||
| 267 | for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { | ||
| 268 | const auto& fence = multi_fence.fences[fence_id]; | ||
| 269 | gpu.WaitFence(fence.id, fence.value); | ||
| 270 | } | ||
| 271 | guard->lock(); | ||
| 272 | |||
| 273 | MicroProfileFlip(); | ||
| 274 | |||
| 275 | // Now send the buffer to the GPU for drawing. | 272 | // Now send the buffer to the GPU for drawing. |
| 276 | // TODO(Subv): Support more than just disp0. The display device selection is probably based | 273 | // TODO(Subv): Support more than just disp0. The display device selection is probably based |
| 277 | // on which display we're drawing (Default, Internal, External, etc) | 274 | // on which display we're drawing (Default, Internal, External, etc) |
| 278 | auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); | 275 | auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); |
| 279 | ASSERT(nvdisp); | 276 | ASSERT(nvdisp); |
| 280 | 277 | ||
| 278 | guard->unlock(); | ||
| 281 | Common::Rectangle<int> crop_rect{ | 279 | Common::Rectangle<int> crop_rect{ |
| 282 | static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), | 280 | static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), |
| 283 | static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; | 281 | static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; |
| 284 | 282 | ||
| 285 | nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), | 283 | nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), |
| 286 | igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), | 284 | igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), |
| 287 | static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect); | 285 | static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect, |
| 286 | buffer.fence.fences, buffer.fence.num_fences); | ||
| 287 | |||
| 288 | MicroProfileFlip(); | ||
| 289 | guard->lock(); | ||
| 288 | 290 | ||
| 289 | swap_interval = buffer.swap_interval; | 291 | swap_interval = buffer.swap_interval; |
| 290 | 292 | ||
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 044ac6ac8..b62615de2 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <vector> | 11 | #include <vector> |
| 12 | 12 | ||
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "core/hle/result.h" | ||
| 14 | #include "core/hle/service/kernel_helpers.h" | 15 | #include "core/hle/service/kernel_helpers.h" |
| 15 | 16 | ||
| 16 | namespace Common { | 17 | namespace Common { |
| @@ -71,8 +72,9 @@ public: | |||
| 71 | 72 | ||
| 72 | /// Gets the vsync event for the specified display. | 73 | /// Gets the vsync event for the specified display. |
| 73 | /// | 74 | /// |
| 74 | /// If an invalid display ID is provided, then nullptr is returned. | 75 | /// If an invalid display ID is provided, then VI::ResultNotFound is returned. |
| 75 | [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); | 76 | /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned. |
| 77 | [[nodiscard]] ResultVal<Kernel::KReadableEvent*> FindVsyncEvent(u64 display_id); | ||
| 76 | 78 | ||
| 77 | /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when | 79 | /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when |
| 78 | /// finished. | 80 | /// finished. |
| @@ -114,6 +116,7 @@ private: | |||
| 114 | void SplitVSync(std::stop_token stop_token); | 116 | void SplitVSync(std::stop_token stop_token); |
| 115 | 117 | ||
| 116 | std::shared_ptr<Nvidia::Module> nvdrv; | 118 | std::shared_ptr<Nvidia::Module> nvdrv; |
| 119 | s32 disp_fd; | ||
| 117 | 120 | ||
| 118 | std::list<VI::Display> displays; | 121 | std::list<VI::Display> displays; |
| 119 | 122 | ||
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index cc679cc81..9e94a462f 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -929,7 +929,7 @@ BSD::BSD(Core::System& system_, const char* name) | |||
| 929 | proxy_packet_received = room_member->BindOnProxyPacketReceived( | 929 | proxy_packet_received = room_member->BindOnProxyPacketReceived( |
| 930 | [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); }); | 930 | [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); }); |
| 931 | } else { | 931 | } else { |
| 932 | LOG_ERROR(Service, "Network isn't initalized"); | 932 | LOG_ERROR(Service, "Network isn't initialized"); |
| 933 | } | 933 | } |
| 934 | } | 934 | } |
| 935 | 935 | ||
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index b34febb50..288aafaaf 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "core/hle/kernel/k_readable_event.h" | 12 | #include "core/hle/kernel/k_readable_event.h" |
| 13 | #include "core/hle/kernel/k_writable_event.h" | 13 | #include "core/hle/kernel/k_writable_event.h" |
| 14 | #include "core/hle/service/kernel_helpers.h" | 14 | #include "core/hle/service/kernel_helpers.h" |
| 15 | #include "core/hle/service/nvdrv/core/container.h" | ||
| 15 | #include "core/hle/service/nvflinger/buffer_item_consumer.h" | 16 | #include "core/hle/service/nvflinger/buffer_item_consumer.h" |
| 16 | #include "core/hle/service/nvflinger/buffer_queue_consumer.h" | 17 | #include "core/hle/service/nvflinger/buffer_queue_consumer.h" |
| 17 | #include "core/hle/service/nvflinger/buffer_queue_core.h" | 18 | #include "core/hle/service/nvflinger/buffer_queue_core.h" |
| @@ -19,6 +20,7 @@ | |||
| 19 | #include "core/hle/service/nvflinger/hos_binder_driver_server.h" | 20 | #include "core/hle/service/nvflinger/hos_binder_driver_server.h" |
| 20 | #include "core/hle/service/vi/display/vi_display.h" | 21 | #include "core/hle/service/vi/display/vi_display.h" |
| 21 | #include "core/hle/service/vi/layer/vi_layer.h" | 22 | #include "core/hle/service/vi/layer/vi_layer.h" |
| 23 | #include "core/hle/service/vi/vi_results.h" | ||
| 22 | 24 | ||
| 23 | namespace Service::VI { | 25 | namespace Service::VI { |
| 24 | 26 | ||
| @@ -28,11 +30,13 @@ struct BufferQueue { | |||
| 28 | std::unique_ptr<android::BufferQueueConsumer> consumer; | 30 | std::unique_ptr<android::BufferQueueConsumer> consumer; |
| 29 | }; | 31 | }; |
| 30 | 32 | ||
| 31 | static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) { | 33 | static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context, |
| 34 | Service::Nvidia::NvCore::NvMap& nvmap) { | ||
| 32 | auto buffer_queue_core = std::make_shared<android::BufferQueueCore>(); | 35 | auto buffer_queue_core = std::make_shared<android::BufferQueueCore>(); |
| 33 | return {buffer_queue_core, | 36 | return { |
| 34 | std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core), | 37 | buffer_queue_core, |
| 35 | std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)}; | 38 | std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), |
| 39 | std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)}; | ||
| 36 | } | 40 | } |
| 37 | 41 | ||
| 38 | Display::Display(u64 id, std::string name_, | 42 | Display::Display(u64 id, std::string name_, |
| @@ -55,18 +59,29 @@ const Layer& Display::GetLayer(std::size_t index) const { | |||
| 55 | return *layers.at(index); | 59 | return *layers.at(index); |
| 56 | } | 60 | } |
| 57 | 61 | ||
| 58 | Kernel::KReadableEvent& Display::GetVSyncEvent() { | 62 | ResultVal<Kernel::KReadableEvent*> Display::GetVSyncEvent() { |
| 59 | return vsync_event->GetReadableEvent(); | 63 | if (got_vsync_event) { |
| 64 | return ResultPermissionDenied; | ||
| 65 | } | ||
| 66 | |||
| 67 | got_vsync_event = true; | ||
| 68 | |||
| 69 | return GetVSyncEventUnchecked(); | ||
| 70 | } | ||
| 71 | |||
| 72 | Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() { | ||
| 73 | return &vsync_event->GetReadableEvent(); | ||
| 60 | } | 74 | } |
| 61 | 75 | ||
| 62 | void Display::SignalVSyncEvent() { | 76 | void Display::SignalVSyncEvent() { |
| 63 | vsync_event->GetWritableEvent().Signal(); | 77 | vsync_event->GetWritableEvent().Signal(); |
| 64 | } | 78 | } |
| 65 | 79 | ||
| 66 | void Display::CreateLayer(u64 layer_id, u32 binder_id) { | 80 | void Display::CreateLayer(u64 layer_id, u32 binder_id, |
| 81 | Service::Nvidia::NvCore::Container& nv_core) { | ||
| 67 | ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); | 82 | ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); |
| 68 | 83 | ||
| 69 | auto [core, producer, consumer] = CreateBufferQueue(service_context); | 84 | auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); |
| 70 | 85 | ||
| 71 | auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); | 86 | auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); |
| 72 | buffer_item_consumer->Connect(false); | 87 | buffer_item_consumer->Connect(false); |
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 3838bb599..33d5f398c 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/hle/result.h" | ||
| 12 | 13 | ||
| 13 | namespace Kernel { | 14 | namespace Kernel { |
| 14 | class KEvent; | 15 | class KEvent; |
| @@ -26,6 +27,11 @@ namespace Service::NVFlinger { | |||
| 26 | class HosBinderDriverServer; | 27 | class HosBinderDriverServer; |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 30 | namespace Service::Nvidia::NvCore { | ||
| 31 | class Container; | ||
| 32 | class NvMap; | ||
| 33 | } // namespace Service::Nvidia::NvCore | ||
| 34 | |||
| 29 | namespace Service::VI { | 35 | namespace Service::VI { |
| 30 | 36 | ||
| 31 | class Layer; | 37 | class Layer; |
| @@ -73,8 +79,16 @@ public: | |||
| 73 | return layers.size(); | 79 | return layers.size(); |
| 74 | } | 80 | } |
| 75 | 81 | ||
| 76 | /// Gets the readable vsync event. | 82 | /** |
| 77 | Kernel::KReadableEvent& GetVSyncEvent(); | 83 | * Gets the internal vsync event. |
| 84 | * | ||
| 85 | * @returns The internal Vsync event if it has not yet been retrieved, | ||
| 86 | * VI::ResultPermissionDenied otherwise. | ||
| 87 | */ | ||
| 88 | [[nodiscard]] ResultVal<Kernel::KReadableEvent*> GetVSyncEvent(); | ||
| 89 | |||
| 90 | /// Gets the internal vsync event. | ||
| 91 | Kernel::KReadableEvent* GetVSyncEventUnchecked(); | ||
| 78 | 92 | ||
| 79 | /// Signals the internal vsync event. | 93 | /// Signals the internal vsync event. |
| 80 | void SignalVSyncEvent(); | 94 | void SignalVSyncEvent(); |
| @@ -84,7 +98,7 @@ public: | |||
| 84 | /// @param layer_id The ID to assign to the created layer. | 98 | /// @param layer_id The ID to assign to the created layer. |
| 85 | /// @param binder_id The ID assigned to the buffer queue. | 99 | /// @param binder_id The ID assigned to the buffer queue. |
| 86 | /// | 100 | /// |
| 87 | void CreateLayer(u64 layer_id, u32 binder_id); | 101 | void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core); |
| 88 | 102 | ||
| 89 | /// Closes and removes a layer from this display with the given ID. | 103 | /// Closes and removes a layer from this display with the given ID. |
| 90 | /// | 104 | /// |
| @@ -118,6 +132,7 @@ private: | |||
| 118 | 132 | ||
| 119 | std::vector<std::unique_ptr<Layer>> layers; | 133 | std::vector<std::unique_ptr<Layer>> layers; |
| 120 | Kernel::KEvent* vsync_event{}; | 134 | Kernel::KEvent* vsync_event{}; |
| 135 | bool got_vsync_event{false}; | ||
| 121 | }; | 136 | }; |
| 122 | 137 | ||
| 123 | } // namespace Service::VI | 138 | } // namespace Service::VI |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 546879648..9c917cacf 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -29,16 +29,12 @@ | |||
| 29 | #include "core/hle/service/service.h" | 29 | #include "core/hle/service/service.h" |
| 30 | #include "core/hle/service/vi/vi.h" | 30 | #include "core/hle/service/vi/vi.h" |
| 31 | #include "core/hle/service/vi/vi_m.h" | 31 | #include "core/hle/service/vi/vi_m.h" |
| 32 | #include "core/hle/service/vi/vi_results.h" | ||
| 32 | #include "core/hle/service/vi/vi_s.h" | 33 | #include "core/hle/service/vi/vi_s.h" |
| 33 | #include "core/hle/service/vi/vi_u.h" | 34 | #include "core/hle/service/vi/vi_u.h" |
| 34 | 35 | ||
| 35 | namespace Service::VI { | 36 | namespace Service::VI { |
| 36 | 37 | ||
| 37 | constexpr Result ERR_OPERATION_FAILED{ErrorModule::VI, 1}; | ||
| 38 | constexpr Result ERR_PERMISSION_DENIED{ErrorModule::VI, 5}; | ||
| 39 | constexpr Result ERR_UNSUPPORTED{ErrorModule::VI, 6}; | ||
| 40 | constexpr Result ERR_NOT_FOUND{ErrorModule::VI, 7}; | ||
| 41 | |||
| 42 | struct DisplayInfo { | 38 | struct DisplayInfo { |
| 43 | /// The name of this particular display. | 39 | /// The name of this particular display. |
| 44 | char display_name[0x40]{"Default"}; | 40 | char display_name[0x40]{"Default"}; |
| @@ -62,6 +58,7 @@ static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); | |||
| 62 | class NativeWindow final { | 58 | class NativeWindow final { |
| 63 | public: | 59 | public: |
| 64 | constexpr explicit NativeWindow(u32 id_) : id{id_} {} | 60 | constexpr explicit NativeWindow(u32 id_) : id{id_} {} |
| 61 | constexpr explicit NativeWindow(const NativeWindow& other) = default; | ||
| 65 | 62 | ||
| 66 | private: | 63 | private: |
| 67 | const u32 magic = 2; | 64 | const u32 magic = 2; |
| @@ -348,7 +345,7 @@ private: | |||
| 348 | if (!layer_id) { | 345 | if (!layer_id) { |
| 349 | LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); | 346 | LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); |
| 350 | IPC::ResponseBuilder rb{ctx, 2}; | 347 | IPC::ResponseBuilder rb{ctx, 2}; |
| 351 | rb.Push(ERR_NOT_FOUND); | 348 | rb.Push(ResultNotFound); |
| 352 | return; | 349 | return; |
| 353 | } | 350 | } |
| 354 | 351 | ||
| @@ -498,7 +495,7 @@ private: | |||
| 498 | if (!display_id) { | 495 | if (!display_id) { |
| 499 | LOG_ERROR(Service_VI, "Display not found! display_name={}", name); | 496 | LOG_ERROR(Service_VI, "Display not found! display_name={}", name); |
| 500 | IPC::ResponseBuilder rb{ctx, 2}; | 497 | IPC::ResponseBuilder rb{ctx, 2}; |
| 501 | rb.Push(ERR_NOT_FOUND); | 498 | rb.Push(ResultNotFound); |
| 502 | return; | 499 | return; |
| 503 | } | 500 | } |
| 504 | 501 | ||
| @@ -554,14 +551,14 @@ private: | |||
| 554 | 551 | ||
| 555 | if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) { | 552 | if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) { |
| 556 | LOG_ERROR(Service_VI, "Invalid scaling mode provided."); | 553 | LOG_ERROR(Service_VI, "Invalid scaling mode provided."); |
| 557 | rb.Push(ERR_OPERATION_FAILED); | 554 | rb.Push(ResultOperationFailed); |
| 558 | return; | 555 | return; |
| 559 | } | 556 | } |
| 560 | 557 | ||
| 561 | if (scaling_mode != NintendoScaleMode::ScaleToWindow && | 558 | if (scaling_mode != NintendoScaleMode::ScaleToWindow && |
| 562 | scaling_mode != NintendoScaleMode::PreserveAspectRatio) { | 559 | scaling_mode != NintendoScaleMode::PreserveAspectRatio) { |
| 563 | LOG_ERROR(Service_VI, "Unsupported scaling mode supplied."); | 560 | LOG_ERROR(Service_VI, "Unsupported scaling mode supplied."); |
| 564 | rb.Push(ERR_UNSUPPORTED); | 561 | rb.Push(ResultNotSupported); |
| 565 | return; | 562 | return; |
| 566 | } | 563 | } |
| 567 | 564 | ||
| @@ -594,7 +591,7 @@ private: | |||
| 594 | if (!display_id) { | 591 | if (!display_id) { |
| 595 | LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); | 592 | LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); |
| 596 | IPC::ResponseBuilder rb{ctx, 2}; | 593 | IPC::ResponseBuilder rb{ctx, 2}; |
| 597 | rb.Push(ERR_NOT_FOUND); | 594 | rb.Push(ResultNotFound); |
| 598 | return; | 595 | return; |
| 599 | } | 596 | } |
| 600 | 597 | ||
| @@ -602,7 +599,7 @@ private: | |||
| 602 | if (!buffer_queue_id) { | 599 | if (!buffer_queue_id) { |
| 603 | LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); | 600 | LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); |
| 604 | IPC::ResponseBuilder rb{ctx, 2}; | 601 | IPC::ResponseBuilder rb{ctx, 2}; |
| 605 | rb.Push(ERR_NOT_FOUND); | 602 | rb.Push(ResultNotFound); |
| 606 | return; | 603 | return; |
| 607 | } | 604 | } |
| 608 | 605 | ||
| @@ -640,7 +637,7 @@ private: | |||
| 640 | if (!layer_id) { | 637 | if (!layer_id) { |
| 641 | LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); | 638 | LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id); |
| 642 | IPC::ResponseBuilder rb{ctx, 2}; | 639 | IPC::ResponseBuilder rb{ctx, 2}; |
| 643 | rb.Push(ERR_NOT_FOUND); | 640 | rb.Push(ResultNotFound); |
| 644 | return; | 641 | return; |
| 645 | } | 642 | } |
| 646 | 643 | ||
| @@ -648,7 +645,7 @@ private: | |||
| 648 | if (!buffer_queue_id) { | 645 | if (!buffer_queue_id) { |
| 649 | LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); | 646 | LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); |
| 650 | IPC::ResponseBuilder rb{ctx, 2}; | 647 | IPC::ResponseBuilder rb{ctx, 2}; |
| 651 | rb.Push(ERR_NOT_FOUND); | 648 | rb.Push(ResultNotFound); |
| 652 | return; | 649 | return; |
| 653 | } | 650 | } |
| 654 | 651 | ||
| @@ -675,19 +672,23 @@ private: | |||
| 675 | IPC::RequestParser rp{ctx}; | 672 | IPC::RequestParser rp{ctx}; |
| 676 | const u64 display_id = rp.Pop<u64>(); | 673 | const u64 display_id = rp.Pop<u64>(); |
| 677 | 674 | ||
| 678 | LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); | 675 | LOG_DEBUG(Service_VI, "called. display_id={}", display_id); |
| 679 | 676 | ||
| 680 | const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); | 677 | const auto vsync_event = nv_flinger.FindVsyncEvent(display_id); |
| 681 | if (!vsync_event) { | 678 | if (vsync_event.Failed()) { |
| 682 | LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); | 679 | const auto result = vsync_event.Code(); |
| 680 | if (result == ResultNotFound) { | ||
| 681 | LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); | ||
| 682 | } | ||
| 683 | |||
| 683 | IPC::ResponseBuilder rb{ctx, 2}; | 684 | IPC::ResponseBuilder rb{ctx, 2}; |
| 684 | rb.Push(ERR_NOT_FOUND); | 685 | rb.Push(result); |
| 685 | return; | 686 | return; |
| 686 | } | 687 | } |
| 687 | 688 | ||
| 688 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 689 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 689 | rb.Push(ResultSuccess); | 690 | rb.Push(ResultSuccess); |
| 690 | rb.PushCopyObjects(vsync_event); | 691 | rb.PushCopyObjects(*vsync_event); |
| 691 | } | 692 | } |
| 692 | 693 | ||
| 693 | void ConvertScalingMode(Kernel::HLERequestContext& ctx) { | 694 | void ConvertScalingMode(Kernel::HLERequestContext& ctx) { |
| @@ -764,7 +765,7 @@ private: | |||
| 764 | return ConvertedScaleMode::PreserveAspectRatio; | 765 | return ConvertedScaleMode::PreserveAspectRatio; |
| 765 | default: | 766 | default: |
| 766 | LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); | 767 | LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode); |
| 767 | return ERR_OPERATION_FAILED; | 768 | return ResultOperationFailed; |
| 768 | } | 769 | } |
| 769 | } | 770 | } |
| 770 | 771 | ||
| @@ -794,7 +795,7 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& | |||
| 794 | if (!IsValidServiceAccess(permission, policy)) { | 795 | if (!IsValidServiceAccess(permission, policy)) { |
| 795 | LOG_ERROR(Service_VI, "Permission denied for policy {}", policy); | 796 | LOG_ERROR(Service_VI, "Permission denied for policy {}", policy); |
| 796 | IPC::ResponseBuilder rb{ctx, 2}; | 797 | IPC::ResponseBuilder rb{ctx, 2}; |
| 797 | rb.Push(ERR_PERMISSION_DENIED); | 798 | rb.Push(ResultPermissionDenied); |
| 798 | return; | 799 | return; |
| 799 | } | 800 | } |
| 800 | 801 | ||
diff --git a/src/core/hle/service/vi/vi_results.h b/src/core/hle/service/vi/vi_results.h new file mode 100644 index 000000000..a46c247d2 --- /dev/null +++ b/src/core/hle/service/vi/vi_results.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/hle/result.h" | ||
| 5 | |||
| 6 | namespace Service::VI { | ||
| 7 | |||
| 8 | constexpr Result ResultOperationFailed{ErrorModule::VI, 1}; | ||
| 9 | constexpr Result ResultPermissionDenied{ErrorModule::VI, 5}; | ||
| 10 | constexpr Result ResultNotSupported{ErrorModule::VI, 6}; | ||
| 11 | constexpr Result ResultNotFound{ErrorModule::VI, 7}; | ||
| 12 | |||
| 13 | } // namespace Service::VI | ||
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index cdf38a2a4..447fbffaa 100644 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp | |||
| @@ -364,7 +364,7 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { | |||
| 364 | std::vector<WSAPOLLFD> host_pollfds(pollfds.size()); | 364 | std::vector<WSAPOLLFD> host_pollfds(pollfds.size()); |
| 365 | std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { | 365 | std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { |
| 366 | WSAPOLLFD result; | 366 | WSAPOLLFD result; |
| 367 | result.fd = fd.socket->fd; | 367 | result.fd = fd.socket->GetFD(); |
| 368 | result.events = TranslatePollEvents(fd.events); | 368 | result.events = TranslatePollEvents(fd.events); |
| 369 | result.revents = 0; | 369 | result.revents = 0; |
| 370 | return result; | 370 | return result; |
| @@ -430,12 +430,12 @@ std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { | |||
| 430 | return {AcceptResult{}, GetAndLogLastError()}; | 430 | return {AcceptResult{}, GetAndLogLastError()}; |
| 431 | } | 431 | } |
| 432 | 432 | ||
| 433 | AcceptResult result; | ||
| 434 | result.socket = std::make_unique<Socket>(); | ||
| 435 | result.socket->fd = new_socket; | ||
| 436 | |||
| 437 | ASSERT(addrlen == sizeof(sockaddr_in)); | 433 | ASSERT(addrlen == sizeof(sockaddr_in)); |
| 438 | result.sockaddr_in = TranslateToSockAddrIn(addr); | 434 | |
| 435 | AcceptResult result{ | ||
| 436 | .socket = std::make_unique<Socket>(new_socket), | ||
| 437 | .sockaddr_in = TranslateToSockAddrIn(addr), | ||
| 438 | }; | ||
| 439 | 439 | ||
| 440 | return {std::move(result), Errno::SUCCESS}; | 440 | return {std::move(result), Errno::SUCCESS}; |
| 441 | } | 441 | } |
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp index 0f0a66160..057fd3661 100644 --- a/src/core/internal_network/network_interface.cpp +++ b/src/core/internal_network/network_interface.cpp | |||
| @@ -188,7 +188,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { | |||
| 188 | std::optional<NetworkInterface> GetSelectedNetworkInterface() { | 188 | std::optional<NetworkInterface> GetSelectedNetworkInterface() { |
| 189 | const auto& selected_network_interface = Settings::values.network_interface.GetValue(); | 189 | const auto& selected_network_interface = Settings::values.network_interface.GetValue(); |
| 190 | const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); | 190 | const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); |
| 191 | if (network_interfaces.size() == 0) { | 191 | if (network_interfaces.empty()) { |
| 192 | LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); | 192 | LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); |
| 193 | return std::nullopt; | 193 | return std::nullopt; |
| 194 | } | 194 | } |
| @@ -206,4 +206,14 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() { | |||
| 206 | return *res; | 206 | return *res; |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | void SelectFirstNetworkInterface() { | ||
| 210 | const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); | ||
| 211 | |||
| 212 | if (network_interfaces.empty()) { | ||
| 213 | return; | ||
| 214 | } | ||
| 215 | |||
| 216 | Settings::values.network_interface.SetValue(network_interfaces[0].name); | ||
| 217 | } | ||
| 218 | |||
| 209 | } // namespace Network | 219 | } // namespace Network |
diff --git a/src/core/internal_network/network_interface.h b/src/core/internal_network/network_interface.h index 9b98b6b42..175e61b1f 100644 --- a/src/core/internal_network/network_interface.h +++ b/src/core/internal_network/network_interface.h | |||
| @@ -24,5 +24,6 @@ struct NetworkInterface { | |||
| 24 | 24 | ||
| 25 | std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); | 25 | std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); |
| 26 | std::optional<NetworkInterface> GetSelectedNetworkInterface(); | 26 | std::optional<NetworkInterface> GetSelectedNetworkInterface(); |
| 27 | void SelectFirstNetworkInterface(); | ||
| 27 | 28 | ||
| 28 | } // namespace Network | 29 | } // namespace Network |
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index 0c746bd82..7d5d37bbc 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "common/zstd_compression.h" | ||
| 9 | #include "core/internal_network/network.h" | 10 | #include "core/internal_network/network.h" |
| 10 | #include "core/internal_network/network_interface.h" | 11 | #include "core/internal_network/network_interface.h" |
| 11 | #include "core/internal_network/socket_proxy.h" | 12 | #include "core/internal_network/socket_proxy.h" |
| @@ -32,8 +33,11 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { | |||
| 32 | return; | 33 | return; |
| 33 | } | 34 | } |
| 34 | 35 | ||
| 36 | auto decompressed = packet; | ||
| 37 | decompressed.data = Common::Compression::DecompressDataZSTD(packet.data); | ||
| 38 | |||
| 35 | std::lock_guard guard(packets_mutex); | 39 | std::lock_guard guard(packets_mutex); |
| 36 | received_packets.push(packet); | 40 | received_packets.push(decompressed); |
| 37 | } | 41 | } |
| 38 | 42 | ||
| 39 | template <typename T> | 43 | template <typename T> |
| @@ -185,6 +189,8 @@ std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flag | |||
| 185 | void ProxySocket::SendPacket(ProxyPacket& packet) { | 189 | void ProxySocket::SendPacket(ProxyPacket& packet) { |
| 186 | if (auto room_member = room_network.GetRoomMember().lock()) { | 190 | if (auto room_member = room_network.GetRoomMember().lock()) { |
| 187 | if (room_member->IsConnected()) { | 191 | if (room_member->IsConnected()) { |
| 192 | packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), | ||
| 193 | packet.data.size()); | ||
| 188 | room_member->SendProxyPacket(packet); | 194 | room_member->SendProxyPacket(packet); |
| 189 | } | 195 | } |
| 190 | } | 196 | } |
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h index a70429b19..2e328c645 100644 --- a/src/core/internal_network/sockets.h +++ b/src/core/internal_network/sockets.h | |||
| @@ -32,6 +32,10 @@ public: | |||
| 32 | std::unique_ptr<SocketBase> socket; | 32 | std::unique_ptr<SocketBase> socket; |
| 33 | SockAddrIn sockaddr_in; | 33 | SockAddrIn sockaddr_in; |
| 34 | }; | 34 | }; |
| 35 | |||
| 36 | SocketBase() = default; | ||
| 37 | explicit SocketBase(SOCKET fd_) : fd{fd_} {} | ||
| 38 | |||
| 35 | virtual ~SocketBase() = default; | 39 | virtual ~SocketBase() = default; |
| 36 | 40 | ||
| 37 | virtual SocketBase& operator=(const SocketBase&) = delete; | 41 | virtual SocketBase& operator=(const SocketBase&) = delete; |
| @@ -89,12 +93,19 @@ public: | |||
| 89 | 93 | ||
| 90 | virtual void HandleProxyPacket(const ProxyPacket& packet) = 0; | 94 | virtual void HandleProxyPacket(const ProxyPacket& packet) = 0; |
| 91 | 95 | ||
| 96 | [[nodiscard]] SOCKET GetFD() const { | ||
| 97 | return fd; | ||
| 98 | } | ||
| 99 | |||
| 100 | protected: | ||
| 92 | SOCKET fd = INVALID_SOCKET; | 101 | SOCKET fd = INVALID_SOCKET; |
| 93 | }; | 102 | }; |
| 94 | 103 | ||
| 95 | class Socket : public SocketBase { | 104 | class Socket : public SocketBase { |
| 96 | public: | 105 | public: |
| 97 | Socket() = default; | 106 | Socket() = default; |
| 107 | explicit Socket(SOCKET fd_) : SocketBase{fd_} {} | ||
| 108 | |||
| 98 | ~Socket() override; | 109 | ~Socket() override; |
| 99 | 110 | ||
| 100 | Socket(const Socket&) = delete; | 111 | Socket(const Socket&) = delete; |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 104d16efa..f24474ed8 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -244,6 +244,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V | |||
| 244 | 244 | ||
| 245 | std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, | 245 | std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, |
| 246 | u64 program_id, std::size_t program_index) { | 246 | u64 program_id, std::size_t program_index) { |
| 247 | if (!file) { | ||
| 248 | return nullptr; | ||
| 249 | } | ||
| 250 | |||
| 247 | FileType type = IdentifyFile(file); | 251 | FileType type = IdentifyFile(file); |
| 248 | const FileType filename_type = GuessFromFilename(file->GetName()); | 252 | const FileType filename_type = GuessFromFilename(file->GetName()); |
| 249 | 253 | ||
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 34ad7cadd..2ac792566 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -551,6 +551,11 @@ struct Memory::Impl { | |||
| 551 | []() {}); | 551 | []() {}); |
| 552 | } | 552 | } |
| 553 | 553 | ||
| 554 | [[nodiscard]] u8* GetPointerSilent(const VAddr vaddr) const { | ||
| 555 | return GetPointerImpl( | ||
| 556 | vaddr, []() {}, []() {}); | ||
| 557 | } | ||
| 558 | |||
| 554 | /** | 559 | /** |
| 555 | * Reads a particular data type out of memory at the given virtual address. | 560 | * Reads a particular data type out of memory at the given virtual address. |
| 556 | * | 561 | * |
| @@ -686,6 +691,10 @@ u8* Memory::GetPointer(VAddr vaddr) { | |||
| 686 | return impl->GetPointer(vaddr); | 691 | return impl->GetPointer(vaddr); |
| 687 | } | 692 | } |
| 688 | 693 | ||
| 694 | u8* Memory::GetPointerSilent(VAddr vaddr) { | ||
| 695 | return impl->GetPointerSilent(vaddr); | ||
| 696 | } | ||
| 697 | |||
| 689 | const u8* Memory::GetPointer(VAddr vaddr) const { | 698 | const u8* Memory::GetPointer(VAddr vaddr) const { |
| 690 | return impl->GetPointer(vaddr); | 699 | return impl->GetPointer(vaddr); |
| 691 | } | 700 | } |
diff --git a/src/core/memory.h b/src/core/memory.h index a11ff8766..81eac448b 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -114,6 +114,7 @@ public: | |||
| 114 | * If the address is not valid, nullptr will be returned. | 114 | * If the address is not valid, nullptr will be returned. |
| 115 | */ | 115 | */ |
| 116 | u8* GetPointer(VAddr vaddr); | 116 | u8* GetPointer(VAddr vaddr); |
| 117 | u8* GetPointerSilent(VAddr vaddr); | ||
| 117 | 118 | ||
| 118 | template <typename T> | 119 | template <typename T> |
| 119 | T* GetPointer(VAddr vaddr) { | 120 | T* GetPointer(VAddr vaddr) { |