diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 86e8bd6027..360cc86796 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -465,7 +465,8 @@ add_library(core STATIC hle/service/am/service/application_functions.cpp hle/service/am/service/application_functions.h hle/service/am/service/application_proxy.cpp - hle/service/am/service/graphics_scale_controller.h + hle/service/am/service/graphics_scaling_parameters_functions.h + hle/service/am/service/performance_functions.h hle/service/am/service/application_proxy.h hle/service/am/service/application_proxy_service.cpp hle/service/am/service/application_proxy_service.h diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h index 571fa65219..3ab467f549 100644 --- a/src/core/hle/service/am/applet.h +++ b/src/core/hle/service/am/applet.h @@ -84,6 +84,7 @@ struct Applet { // Application functions bool game_play_recording_supported{}; bool media_playback_state{}; + bool cpu_overclock_requested{}; ///< Switch 2 IPerformanceFunctions GamePlayRecordingState game_play_recording_state{GamePlayRecordingState::Disabled}; bool jit_service_launched{}; bool application_crash_report_enabled{}; diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp index cd5be56363..26250bb4b5 100644 --- a/src/core/hle/service/am/service/application_functions.cpp +++ b/src/core/hle/service/am/service/application_functions.cpp @@ -122,7 +122,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ {200, &IApplicationFunctions::StubReplyZeroed, "GetLastApplicationExitReason"}, {210, D<&IApplicationFunctions::GetOperationModeRefreshEvent>, "GetOperationModeRefreshEvent (guessed)"}, {220, &IApplicationFunctions::StubReplyZeroed, "Unknown220"}, // [20.0.0+] - {230, &IApplicationFunctions::StubReplyZeroed, "Unknown230"}, + {230, D<&IApplicationFunctions::IsTensorRtSupported>, "IsTensorRtSupported"}, {300, &IApplicationFunctions::StubReplyInterface, "CreateMovieWriter"}, // [19.0.0+] {310, &IApplicationFunctions::StubReplyZeroed, "Unknown310"}, // [20.0.0+] {320, D<&IApplicationFunctions::Unknown320>, "Unknown320"}, // [20.0.0+] @@ -576,6 +576,13 @@ Result IApplicationFunctions::GetOperationModeRefreshEvent( R_SUCCEED(); } +Result IApplicationFunctions::IsTensorRtSupported(Out out_is_supported) { + // Switch 2 hardware runs TensorRT networks (DLSS); the zeroed stub told titles it can't. + LOG_DEBUG(Service_AM, "called"); + *out_is_supported = true; + R_SUCCEED(); +} + Result IApplicationFunctions::Unknown320(Out out) { // Called by Switch 2 titles during nn::oe::Initialize. It returns two bytes of data (meaning // unknown); a reply without them left the caller reading stale message words. diff --git a/src/core/hle/service/am/service/application_functions.h b/src/core/hle/service/am/service/application_functions.h index 8acccab293..e3d048588d 100644 --- a/src/core/hle/service/am/service/application_functions.h +++ b/src/core/hle/service/am/service/application_functions.h @@ -85,6 +85,7 @@ class IApplicationFunctions final : public ServiceFramework out_event); Result CreateApplicationAndRequestToStart(u64 application_id); Result GetOperationModeRefreshEvent(OutCopyHandle out_event); + Result IsTensorRtSupported(Out out_is_supported); Result Unknown320(Out out); Result Unknown321(Out out); Result Unknown330(Out out); diff --git a/src/core/hle/service/am/service/application_proxy.cpp b/src/core/hle/service/am/service/application_proxy.cpp index 430f3c6888..d5814a073a 100644 --- a/src/core/hle/service/am/service/application_proxy.cpp +++ b/src/core/hle/service/am/service/application_proxy.cpp @@ -11,8 +11,9 @@ #include "core/hle/service/am/service/common_state_getter.h" #include "core/hle/service/am/service/debug_functions.h" #include "core/hle/service/am/service/display_controller.h" -#include "core/hle/service/am/service/graphics_scale_controller.h" +#include "core/hle/service/am/service/graphics_scaling_parameters_functions.h" #include "core/hle/service/am/service/library_applet_creator.h" +#include "core/hle/service/am/service/performance_functions.h" #include "core/hle/service/am/service/process_winding_controller.h" #include "core/hle/service/am/service/self_controller.h" #include "core/hle/service/am/service/window_controller.h" @@ -35,10 +36,11 @@ IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr, "GetProcessWindingController"}, {11, D<&IApplicationProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"}, {20, D<&IApplicationProxy::GetApplicationFunctions>, "GetApplicationFunctions"}, - // Switch 2-only getters; see nx2_stub_interface.h. - {40, &IApplicationProxy::GetNx2Interface, "Nx2Getter40"}, + // Switch 2-only getters; see nx2_stub_interface.h. Switchbrew leaves 40 and 60 unnamed: + // their names follow the interfaces they return. + {40, D<&IApplicationProxy::GetPerformanceFunctions>, "GetPerformanceFunctions"}, {50, &IApplicationProxy::GetNx2Interface, "Nx2Getter50"}, - {60, D<&IApplicationProxy::GetGraphicsScaleController>, "GetGraphicsScaleController (Switch 2)"}, + {60, D<&IApplicationProxy::GetGraphicsScalingParametersFunctions>, "GetGraphicsScalingParametersFunctions"}, {70, &IApplicationProxy::GetNx2Interface, "Nx2Getter70"}, {80, &IApplicationProxy::GetNx2Interface, "Nx2Getter80"}, {90, &IApplicationProxy::GetNx2Interface, "Nx2Getter90"}, @@ -51,10 +53,17 @@ IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr> out_controller) { +Result IApplicationProxy::GetPerformanceFunctions( + Out> out_functions) { LOG_DEBUG(Service_AM, "called"); - *out_controller = std::make_shared(system); + *out_functions = std::make_shared(system, m_applet); + R_SUCCEED(); +} + +Result IApplicationProxy::GetGraphicsScalingParametersFunctions( + Out> out_functions) { + LOG_DEBUG(Service_AM, "called"); + *out_functions = std::make_shared(system); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/application_proxy.h b/src/core/hle/service/am/service/application_proxy.h index 0a706be4fd..ceed41b792 100644 --- a/src/core/hle/service/am/service/application_proxy.h +++ b/src/core/hle/service/am/service/application_proxy.h @@ -17,8 +17,9 @@ class IApplicationFunctions; class ICommonStateGetter; class IDebugFunctions; class IDisplayController; -class IGraphicsScaleController; +class IGraphicsScalingParametersFunctions; class ILibraryAppletCreator; +class IPerformanceFunctions; class IProcessWindingController; class ISelfController; class IWindowController; @@ -32,7 +33,9 @@ class IApplicationProxy final : public ServiceFramework { private: void GetNx2Interface(HLERequestContext& ctx); - Result GetGraphicsScaleController(Out> out_controller); + Result GetPerformanceFunctions(Out> out_functions); + Result GetGraphicsScalingParametersFunctions( + Out> out_functions); Result GetAudioController(Out> out_audio_controller); Result GetDisplayController(Out> out_display_controller); Result GetProcessWindingController( diff --git a/src/core/hle/service/am/service/application_proxy_service.cpp b/src/core/hle/service/am/service/application_proxy_service.cpp index 9d332798b4..67030c0c7f 100644 --- a/src/core/hle/service/am/service/application_proxy_service.cpp +++ b/src/core/hle/service/am/service/application_proxy_service.cpp @@ -19,9 +19,9 @@ IApplicationProxyService::IApplicationProxyService(Core::System& system_, : ServiceFramework{system_, "appletOE"}, m_window_system{window_system} { static const FunctionInfo functions[] = { {0, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy"}, - // Switch 2 SDKs open the proxy with command 10, same arguments. - {10, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy (Switch 2)"}, - {2000, &IApplicationProxyService::StubReplyInterface, "Unknown2000"}, + // Switch 2 SDKs open the proxy with OpenApplicationProxy2, same arguments. + {10, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy2"}, + {2000, &IApplicationProxyService::StubReplyInterface, "OpenCompatApplicationFunctions"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/am/service/common_state_getter.cpp b/src/core/hle/service/am/service/common_state_getter.cpp index 2c5e536414..7191bf7ec4 100644 --- a/src/core/hle/service/am/service/common_state_getter.cpp +++ b/src/core/hle/service/am/service/common_state_getter.cpp @@ -79,8 +79,8 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr, "Unknown610"}, //21.0.0+ {611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+ - {650, &ICommonStateGetter::StubReplyZeroed, "Unknown650"}, - {651, &ICommonStateGetter::StubReplyZeroed, "Unknown651"}, + {650, &ICommonStateGetter::StubReplyZeroed, "BeginUsingCameraResource"}, + {651, &ICommonStateGetter::StubReplyZeroed, "EndUsingCameraResource"}, {900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+ {910, &ICommonStateGetter::StubReplyZeroed, "GetLaunchRequiredTick"}, //17.0.0+ {1000, D<&ICommonStateGetter::BeginVrMode3d>, "BeginVrMode3d"}, //19.0.0+ @@ -89,8 +89,8 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr, "GetVrLaboGoggleViewport"}, //21.0.0+ {1004, D<&ICommonStateGetter::GetPanelPhysicalSizeForSpecificTitle>, "GetPanelPhysicalSizeForSpecificTitle"}, //21.0.0+ {1005, D<&ICommonStateGetter::GetPanelResolutionForSpecificTitle>, "GetPanelResolutionForSpecificTitle"}, //21.0.0+ - {2000, &ICommonStateGetter::StubReplyZeroed, "Unknown2000"}, - {2010, &ICommonStateGetter::StubReplyZeroed, "Unknown2010"}, + {2000, &ICommonStateGetter::StubReplyZeroed, "SetRtcModeChangedMessageEnabled"}, + {2010, &ICommonStateGetter::StubReplyZeroed, "GetCurrentDisplayLayoutRatio"}, }; // clang-format on diff --git a/src/core/hle/service/am/service/graphics_scale_controller.h b/src/core/hle/service/am/service/graphics_scale_controller.h deleted file mode 100644 index b31858d3ed..0000000000 --- a/src/core/hle/service/am/service/graphics_scale_controller.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "core/hle/service/cmif_types.h" -#include "core/hle/service/nx2_stub_interface.h" -#include "core/hle/service/service.h" - -namespace Service::AM { - -// Switch 2 interface returned by IApplicationProxy command 60 (name is a guess). nn::oe's -// GetCurrentGraphicsScale reads command 10 and accepts 10, 20 or 30 (scale enum 0, 1, 2). -// Commands without a handler are auto-stubbed because the name carries the nx2: prefix. -class IGraphicsScaleController final : public ServiceFramework { -public: - explicit IGraphicsScaleController(Core::System& system_) - : ServiceFramework{system_, "nx2:IGraphicsScaleController"} { - // clang-format off - static const FunctionInfo functions[] = { - {10, D<&IGraphicsScaleController::GetCurrentGraphicsScale>, "GetCurrentGraphicsScale"}, - }; - // clang-format on - RegisterHandlers(functions); - } - -private: - // The reply is 12 bytes; only the first word (the scale) is known. The other two used to be - // left to the message buffer's leftovers. - Result GetCurrentGraphicsScale(Out out_scale, Out out_unknown1, Out out_unknown2) { - LOG_WARNING(Service_AM, "(STUBBED) called, returning 10"); - *out_scale = 10; - *out_unknown1 = 0; - *out_unknown2 = 0; - R_SUCCEED(); - } -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/graphics_scaling_parameters_functions.h b/src/core/hle/service/am/service/graphics_scaling_parameters_functions.h new file mode 100644 index 0000000000..e3632cba45 --- /dev/null +++ b/src/core/hle/service/am/service/graphics_scaling_parameters_functions.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +// nn::am::service::IGraphicsScalingParametersFunctions (Switch 2), returned by IApplicationProxy +// command 60. nn::oe's GetCurrentGraphicsScale reads command 10 and accepts 10, 20 or 30 (scale +// enum 0, 1, 2). Commands without a handler are auto-stubbed because the name carries the nx2: +// prefix. +class IGraphicsScalingParametersFunctions final + : public ServiceFramework { +public: + explicit IGraphicsScalingParametersFunctions(Core::System& system_) + : ServiceFramework{system_, "nx2:IGraphicsScalingParametersFunctions"} { + // clang-format off + static const FunctionInfo functions[] = { + {10, D<&IGraphicsScalingParametersFunctions::GetGraphicsScalingParameters>, "GetGraphicsScalingParameters"}, + {1050, D<&IGraphicsScalingParametersFunctions::SetGraphicsScaleChangedNotificationEnabled>, "SetGraphicsScaleChangedNotificationEnabled"}, + }; + // clang-format on + RegisterHandlers(functions); + } + +private: + // The reply is 12 bytes; only the first word (the scale) is known. The other two used to be + // left to the message buffer's leftovers. + Result GetGraphicsScalingParameters(Out out_scale, Out out_unknown1, + Out out_unknown2) { + LOG_WARNING(Service_AM, "(STUBBED) called, returning 10"); + *out_scale = 10; + *out_unknown1 = 0; + *out_unknown2 = 0; + R_SUCCEED(); + } + + // 1050(bool): every title calls it at boot. The scale never changes here, so there is never a + // change message to send either way. + Result SetGraphicsScaleChangedNotificationEnabled(bool enabled) { + LOG_DEBUG(Service_AM, "called, enabled={}", enabled); + R_SUCCEED(); + } +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/performance_functions.h b/src/core/hle/service/am/service/performance_functions.h new file mode 100644 index 0000000000..1eac4ddd0e --- /dev/null +++ b/src/core/hle/service/am/service/performance_functions.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "common/settings.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +// nn::am::service::IPerformanceFunctions (Switch 2), returned by IApplicationProxy command 40. The +// application's own request is kept on its applet (so it survives the interface being opened +// again) and read back, and Eden's CPU clock setting (Boost or Overclock) also reads as enabled. +// The request doesn't change that setting: it scales the guest clock, which would jump under a +// running title. Commands without a handler are auto-stubbed because the name carries the nx2: +// prefix. +class IPerformanceFunctions final : public ServiceFramework { +public: + explicit IPerformanceFunctions(Core::System& system_, std::shared_ptr applet) + : ServiceFramework{system_, "nx2:IPerformanceFunctions"}, m_applet{std::move(applet)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IPerformanceFunctions::IsCpuOverclockEnabled>, "IsCpuOverclockEnabled"}, + {1, D<&IPerformanceFunctions::SetCpuOverclockEnabled>, "SetCpuOverclockEnabled"}, + }; + // clang-format on + RegisterHandlers(functions); + } + +private: + Result IsCpuOverclockEnabled(Out out_is_enabled) { + std::scoped_lock lk{m_applet->lock}; + *out_is_enabled = m_applet->cpu_overclock_requested || + Settings::values.cpu_clock.GetValue() != Settings::CpuClock::Normal; + LOG_DEBUG(Service_AM, "called, enabled={}", *out_is_enabled); + R_SUCCEED(); + } + + Result SetCpuOverclockEnabled(bool enabled) { + LOG_DEBUG(Service_AM, "called, enabled={}", enabled); + std::scoped_lock lk{m_applet->lock}; + m_applet->cpu_overclock_requested = enabled; + R_SUCCEED(); + } + + const std::shared_ptr m_applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/service/self_controller.cpp b/src/core/hle/service/am/service/self_controller.cpp index d67e1b32cd..2616dee609 100644 --- a/src/core/hle/service/am/service/self_controller.cpp +++ b/src/core/hle/service/am/service/self_controller.cpp @@ -42,9 +42,9 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr {19, D<&ISelfController::SetAlbumImageOrientation>, "SetAlbumImageOrientation"}, {20, &ISelfController::StubReplyZeroed, "SetDesirableKeyboardLayout"}, {21, &ISelfController::StubReplyZeroed, "GetScreenShotProgramId"}, - {22, &ISelfController::StubReplyZeroed, "Unknown22"}, - {23, &ISelfController::StubReplyZeroed, "Unknown23"}, - {24, &ISelfController::StubReplyZeroed, "Unknown24"}, + {22, &ISelfController::StubReplyZeroed, "GetScreenShotAcdIndex"}, + {23, &ISelfController::StubReplyZeroed, "GetScreenShotApparentPlatform"}, + {24, &ISelfController::StubReplyZeroed, "GetScreenShotApplicationProperty"}, {40, D<&ISelfController::CreateManagedDisplayLayer>, "CreateManagedDisplayLayer"}, {41, D<&ISelfController::IsSystemBufferSharingEnabled>, "IsSystemBufferSharingEnabled"}, {42, D<&ISelfController::GetSystemSharedLayerHandle>, "GetSystemSharedLayerHandle"}, @@ -83,12 +83,12 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr {230, D<&ISelfController::Unknown230>, "Unknown230"}, {240, D<&ISelfController::Unknown240>, "Unknown240"}, {1000, &ISelfController::StubReplyInterface, "GetDebugStorageChannel"}, - {2000, &ISelfController::GetNx2GraphicsSystemSession, "GetGraphicsSystemSession (Switch 2, guessed)"}, - {2100, &ISelfController::StubReplyZeroed, "Unknown2100"}, - {2200, &ISelfController::StubReplyZeroed, "Unknown2200"}, - {2300, &ISelfController::StubReplyZeroed, "Unknown2300"}, - {2400, &ISelfController::StubReplyZeroed, "Unknown2400"}, - {2401, &ISelfController::StubReplyZeroed, "Unknown2401"}, + {2000, &ISelfController::GetGraphicsSupervisor, "GetGraphicsSupervisor"}, + {2100, &ISelfController::StubReplyZeroed, "SetRecordingLayerEnabled"}, + {2200, &ISelfController::StubReplyZeroed, "SetSharingLayerEnabled"}, + {2300, &ISelfController::StubReplyZeroed, "SetCopyrightLayerEnabled"}, + {2400, &ISelfController::StubReplyZeroed, "SetRtcScreenSharingAudioEnabled"}, + {2401, &ISelfController::StubReplyZeroed, "SetStreamPlayMirroringAudioEnabled"}, {2402, &ISelfController::StubReplyZeroed, "Unknown2402"}, }; // clang-format on @@ -100,9 +100,9 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr m_applet->library_applet_mode); } -void ISelfController::GetNx2GraphicsSystemSession(HLERequestContext& ctx) { +void ISelfController::GetGraphicsSupervisor(HLERequestContext& ctx) { // nn::gsv::BootWithRemoteMemory sends TIPC on the handle this returns. - LOG_WARNING(Service_AM, "(STUBBED) Switch 2 ISelfController::2000, returning a nx2:gsv session"); + LOG_WARNING(Service_AM, "(STUBBED) GetGraphicsSupervisor, returning a nx2:gsv session"); ReturnRawSession(system, ctx, std::make_shared(system)); } diff --git a/src/core/hle/service/am/service/self_controller.h b/src/core/hle/service/am/service/self_controller.h index 221fbb0981..7cc0a156fb 100644 --- a/src/core/hle/service/am/service/self_controller.h +++ b/src/core/hle/service/am/service/self_controller.h @@ -30,7 +30,7 @@ class ISelfController final : public ServiceFramework { ~ISelfController() override; private: - void GetNx2GraphicsSystemSession(HLERequestContext& ctx); + void GetGraphicsSupervisor(HLERequestContext& ctx); Result Exit(); Result LockExit(); Result UnlockExit(); diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index eb1511857b..f41ccff9ac 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -206,9 +206,9 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService("audrec:u", std::make_shared(system), 30); server_manager->RegisterNamedService("audren:u", std::make_shared(system), 30); // Switch 2 audio entry point (nn::audio::CreateUserClientSessionByHipc). - server_manager->RegisterNamedService("aud:u", std::make_shared(system), 30); - server_manager->RegisterNamedService("auddmg:u", std::make_shared(system), 30); - server_manager->RegisterNamedService("audsmx:u", std::make_shared(system), 30); + server_manager->RegisterNamedService("aud:u", std::make_shared(system), 30); + server_manager->RegisterNamedService("auddmg:u", std::make_shared(system), 30); + server_manager->RegisterNamedService("audsmx:u", std::make_shared(system), 30); server_manager->RegisterNamedService("audren:a", std::make_shared(system), 30); server_manager->RegisterNamedService("hwopus", std::make_shared(system), 25); ServerManager::RunServer(std::move(server_manager)); diff --git a/src/core/hle/service/audio/nx2_audio_device_manager.h b/src/core/hle/service/audio/nx2_audio_device_manager.h index 675012b0ab..f9bcb4b3af 100644 --- a/src/core/hle/service/audio/nx2_audio_device_manager.h +++ b/src/core/hle/service/audio/nx2_audio_device_manager.h @@ -72,10 +72,12 @@ inline std::shared_ptr Nx2SysmixStreamList() { return list; } -// Switch 2 virtual output devices (nn::audio::ListVirtualOutputDevices and friends), reached -// through the object auddmg:u command 0 returns. Eden plays everything through one stereo 48 kHz -// output, so there is one virtual device. The nx2: name keeps the other commands auto-stubbed and -// logged. +namespace Devmgr { + +// nn::audio::devmgr::IUserSession: Switch 2 virtual output devices +// (nn::audio::ListVirtualOutputDevices and friends), reached through the object auddmg:u command 0 +// returns. Eden plays everything through one stereo 48 kHz output, so there is one virtual device. +// The nx2: name keeps the other commands auto-stubbed and logged. // - 0(u32): u32 count, the device names in the out buffer (0x100 bytes each). // - 100(u32): the default device's name in the out buffer. // - 300(16-byte stream format, name buffer): whether the format is supported (result only). @@ -86,27 +88,27 @@ inline std::shared_ptr Nx2SysmixStreamList() { // - 700(u32 stream id, u32 device functionality, name buffer): attach a sysmix stream to the // device, which starts it (nn::audio::devmgr::AttachStream, behind OutputStream Start). // - 800(u32 stream id): detach it again (Stop). -class INx2AudioVirtualDeviceManager final : public ServiceFramework { +class IUserSession final : public ServiceFramework { public: - explicit INx2AudioVirtualDeviceManager(Core::System& system_) + explicit IUserSession(Core::System& system_) : ServiceFramework{system_, "nx2:auddmg:u:manager"}, service_context{system_, "nx2:auddmg:u:manager"}, state_event{service_context.CreateEvent("nx2:auddmg:u:StateEvent")} { // clang-format off static const FunctionInfo functions[] = { - {0, &INx2AudioVirtualDeviceManager::ListVirtualOutputDevices, "ListVirtualOutputDevices"}, - {100, &INx2AudioVirtualDeviceManager::GetDefaultVirtualOutputDevice, "GetDefaultVirtualOutputDevice"}, - {300, &INx2AudioVirtualDeviceManager::IsStreamFormatSupported, "IsVirtualOutputDeviceStreamFormatSupported"}, - {401, &INx2AudioVirtualDeviceManager::GetVirtualOutputDeviceProperties, "GetVirtualOutputDeviceProperties"}, - {500, &INx2AudioVirtualDeviceManager::AcquireStateChangeEvent, "AcquireVirtualOutputDeviceStateEvent (guessed)"}, - {700, &INx2AudioVirtualDeviceManager::AttachStream, "AttachStream"}, - {800, &INx2AudioVirtualDeviceManager::DetachStream, "DetachStream"}, + {0, &IUserSession::ListVirtualOutputDevices, "ListVirtualOutputDevices"}, + {100, &IUserSession::GetDefaultVirtualOutputDevice, "GetDefaultVirtualOutputDevice"}, + {300, &IUserSession::IsStreamFormatSupported, "IsVirtualOutputDeviceStreamFormatSupported"}, + {401, &IUserSession::GetVirtualOutputDeviceProperties, "GetVirtualOutputDeviceProperties"}, + {500, &IUserSession::AcquireStateChangeEvent, "AcquireVirtualOutputDeviceStateEvent (guessed)"}, + {700, &IUserSession::AttachStream, "AttachStream"}, + {800, &IUserSession::DetachStream, "DetachStream"}, }; // clang-format on RegisterHandlers(functions); } - ~INx2AudioVirtualDeviceManager() override { + ~IUserSession() override { service_context.CloseEvent(state_event); } @@ -213,38 +215,44 @@ class INx2AudioVirtualDeviceManager final : public ServiceFramework { +// Switch 2 "auddmg:u" (nn::audio::devmgr::IUserSessionManager): command 0 (16 bytes and the pid) +// returns the user session. +class IUserSessionManager final : public ServiceFramework { public: - explicit INx2AudioDeviceManagerService(Core::System& system_) + explicit IUserSessionManager(Core::System& system_) : ServiceFramework{system_, "nx2:auddmg:u"} { // clang-format off static const FunctionInfo functions[] = { - {0, &INx2AudioDeviceManagerService::OpenVirtualDeviceManager, "OpenVirtualDeviceManager (guessed)"}, + {0, &IUserSessionManager::OpenUserSession, "OpenUserSession"}, }; // clang-format on RegisterHandlers(functions); } private: - void OpenVirtualDeviceManager(HLERequestContext& ctx) { + void OpenUserSession(HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(ctx, system); + rb.PushIpcInterface(ctx, system); } }; -// The stream object IUserSession command 1 returns. Commands (ids, request -> reply): +} // namespace Devmgr + +namespace Sysmix { + +// nn::audio::sysmix::IExternalStreamSession, the stream object IUserSession command 1 returns. +// Commands (ids, request -> reply): // - 0(u32 sample type, u32 channels, u32 sample rate, u32 frames, u32 prefill, u32 flags): open. // - 1: close. 2: u32 state. 3: f32 gain. 4(f32 gain, s64 ramp ns): set the gain. // - 5 / 6: acquire (copy handle) / release the state-change event. // - 7(u32 mode, s32 threshold) / 8: acquire (copy handle) / release the data event. // - 9(s64 ns): clock the stream. 10(s32 frames) -> u32: prime. 11(s32 frames) -> u32: purge. // - 12(name buffer): rename. -class INx2SysmixStreamSession final : public ServiceFramework { +class IExternalStreamSession final : public ServiceFramework { public: - explicit INx2SysmixStreamSession(Core::System& system_, + explicit IExternalStreamSession(Core::System& system_, std::shared_ptr streams_, std::shared_ptr stream_) : ServiceFramework{system_, "nx2:audsmx:u:stream"}, @@ -252,19 +260,19 @@ class INx2SysmixStreamSession final : public ServiceFrameworkdata_event = service_context.CreateEvent("nx2:audsmx:u:StreamDataEvent"); } - ~INx2SysmixStreamSession() override { + ~IExternalStreamSession() override { // The drain timer signals these events under the lock: unlist the stream first. { std::scoped_lock lk{streams->mutex}; @@ -431,19 +439,19 @@ class INx2SysmixStreamSession final : public ServiceFramework stream; }; -// The session audsmx:u command 0 returns. Commands: +// nn::audio::sysmix::IUserSession, the session audsmx:u command 0 returns. Commands: // - 1(u32, u32 creation flags, u64 pid, u64 buffer address, u64 buffer size, name buffer): // u32 stream id and the stream object. // - 2 / 3(buffer of u32 stream ids): engage / disengage those streams. -class INx2SysmixUserSession final : public ServiceFramework { +class IUserSession final : public ServiceFramework { public: - explicit INx2SysmixUserSession(Core::System& system_) + explicit IUserSession(Core::System& system_) : ServiceFramework{system_, "nx2:audsmx:u:session"} { // clang-format off static const FunctionInfo functions[] = { - {1, &INx2SysmixUserSession::CreateStream, "CreateStream"}, - {2, &INx2SysmixUserSession::EngageStreams, "EngageStreams"}, - {3, &INx2SysmixUserSession::DisengageStreams, "DisengageStreams"}, + {1, &IUserSession::CreateStream, "CreateStream"}, + {2, &IUserSession::EngageStreams, "EngageStreams"}, + {3, &IUserSession::DisengageStreams, "DisengageStreams"}, }; // clang-format on RegisterHandlers(functions); @@ -457,7 +465,7 @@ class INx2SysmixUserSession final : public ServiceFrameworkid); - rb.PushIpcInterface(ctx, system, streams, stream); + rb.PushIpcInterface(ctx, system, streams, stream); } void SetEngaged(HLERequestContext& ctx, bool engaged) { @@ -614,14 +622,15 @@ class INx2SysmixUserSession final : public ServiceFramework drain_timer; }; -// Switch 2 "audsmx:u": command 0 (16 bytes, the pid and a copy handle) returns the user session. -class INx2AudioSoundMixerService final : public ServiceFramework { +// Switch 2 "audsmx:u" (nn::audio::sysmix::IUserSessionManager): command 0 (16 bytes, the pid and +// a copy handle) returns the user session. +class IUserSessionManager final : public ServiceFramework { public: - explicit INx2AudioSoundMixerService(Core::System& system_) + explicit IUserSessionManager(Core::System& system_) : ServiceFramework{system_, "nx2:audsmx:u"} { // clang-format off static const FunctionInfo functions[] = { - {0, &INx2AudioSoundMixerService::OpenUserSession, "OpenUserSession"}, + {0, &IUserSessionManager::OpenUserSession, "OpenUserSession"}, }; // clang-format on RegisterHandlers(functions); @@ -632,8 +641,10 @@ class INx2AudioSoundMixerService final : public ServiceFramework(ctx, system); + rb.PushIpcInterface(ctx, system); } }; +} // namespace Sysmix + } // namespace Service::Audio diff --git a/src/core/hle/service/audio/nx2_audio_user_client.h b/src/core/hle/service/audio/nx2_audio_user_client.h index f053ad62af..3392535160 100644 --- a/src/core/hle/service/audio/nx2_audio_user_client.h +++ b/src/core/hle/service/audio/nx2_audio_user_client.h @@ -11,20 +11,20 @@ namespace Service::Audio { -// Switch 2 "aud:u" (nn::audio::CreateUserClientSessionByHipc). The SDK reaches the Switch 1 -// audio interfaces through it: command 2 returns an object used exactly like +// Switch 2 "aud:u", nn::audio::IUserClientSession (nn::audio::CreateUserClientSessionByHipc). +// The SDK reaches the Switch 1 audio interfaces through it: command 2 returns an object used like // IAudioRendererManager (vtable slot 1 = GetWorkBufferSize with a Switch 1 parameter block), so // it is backed by Eden's audren:u implementation. Command 1 is nn::audio::OpenAudioOut's // manager, called like IAudioOutManager (slot 1 = OpenAudioOut), backed by audout:u. The nx2: name // keeps unknown commands auto-stubbed and logged. -class INx2AudioUserClient final : public ServiceFramework { +class IUserClientSession final : public ServiceFramework { public: - explicit INx2AudioUserClient(Core::System& system_) + explicit IUserClientSession(Core::System& system_) : ServiceFramework{system_, "nx2:aud:u"} { // clang-format off static const FunctionInfo functions[] = { - {1, &INx2AudioUserClient::GetAudioOutManager, "GetAudioOutManager (guessed)"}, - {2, &INx2AudioUserClient::GetAudioRendererManager, "GetAudioRendererManager (guessed)"}, + {1, &IUserClientSession::GetAudioOutManager, "GetAudioOutManager (guessed)"}, + {2, &IUserClientSession::GetAudioRendererManager, "GetAudioRendererManager (guessed)"}, }; // clang-format on RegisterHandlers(functions); diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index bfb2d6b503..423b49eca0 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/logging.h" #include "common/uuid.h" @@ -14,6 +15,7 @@ #include "core/hle/service/friend/friend_interface.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nx2_stub_interface.h" #include "core/hle/service/server_manager.h" namespace Service::Friend { @@ -558,53 +560,122 @@ class IServiceForSystem final : public ServiceFramework { } }; -// Session returned by chat:u 0. Command 1 hands back a handle the title waits on, so it gets an -// event that is never signalled; everything else falls through to the nx2 auto stub. +// Session returned by chat:u 0 and 1 (nn::chat::IUserShimScopedObject). Command 1 hands back a +// handle the title waits on; 2-4 attach to the room info, local camera image processing mode and +// local camera zoom change events (Switchbrew: no input, an event out). None of them fires outside +// a GameChat room, so each gets the never-signalled stub event. The other named commands answer 0; +// everything else falls through to the nx2 auto stub. class IChatSession final : public ServiceFramework { public: - explicit IChatSession(Core::System& system_) - : ServiceFramework{system_, "nx2:chat:u::0"}, service_context{system_, "IChatSession"} { + explicit IChatSession(Core::System& system_) : ServiceFramework{system_, "nx2:chat:u::0"} { static const FunctionInfo functions[] = { - {1, &IChatSession::GetEvent, "GetEvent (guessed)"}, + {1, &IChatSession::StubReplyEvent, "GetEvent (guessed)"}, + {2, &IChatSession::StubReplyEvent, "AttachToRoomInfoChangedEvent"}, + {3, &IChatSession::StubReplyEvent, "AttachToLocalCameraImageProcessingModeChangedEvent"}, + {4, &IChatSession::StubReplyEvent, "AttachToLocalCameraZoomMagnificationChangedEvent"}, + {10, &IChatSession::StubReplyZeroed, "StartLocalCameraStreaming"}, + {11, &IChatSession::StubReplyZeroed, "StopLocalCameraStreaming"}, + {20, &IChatSession::StubReplyZeroed, "StartRemoteCameraStreaming"}, + {21, &IChatSession::StubReplyZeroed, "StopRemoteCameraStreaming"}, + {30, &IChatSession::StubReplyZeroed, "CreateAudioStreamWorkBuffer"}, + {31, &IChatSession::StubReplyZeroed, "DestroyAudioStreamWorkBuffer"}, + {32, &IChatSession::StubReplyZeroed, "GetAudioStreamCapacityFrameCount"}, + {33, &IChatSession::StubReplyZeroed, "StartAudioStreamRecording"}, + {34, &IChatSession::StubReplyZeroed, "StopAudioStreamRecording"}, + {35, &IChatSession::StubReplyZeroed, "GetAudioStreamFreeFrameCount"}, + {36, &IChatSession::StubReplyZeroed, "GetAudioStreamFilledFrameCount"}, + {37, &IChatSession::StubReplyZeroed, "ReadAudioStream"}, }; RegisterHandlers(functions); - event = service_context.CreateEvent("IChatSession:Event"); - } - - ~IChatSession() override { - service_context.CloseEvent(event); } +}; -private: - void GetEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_Friend, "(STUBBED) chat:u::0 1 [5]={:#x}", ctx.CommandBuffer()[5]); - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(ctx, event->GetReadableEvent()); +// nn::chat::IUserLayoutCustomizationShimScopedObject, returned by chat:u 2. Per Switchbrew: +// 0 Initialize(pid, u64), then 1 hands back an event official software takes right after it; +// 10, 11 (u64 out), 13 (u8 and 16 bytes out) and 20 (u8 out) are unnamed, 12 ends the full-screen +// section. Outside a GameChat room the layout never changes: the event never fires and the rest +// answer 0. +class IUserLayoutCustomizationShimScopedObject final + : public ServiceFramework { +public: + explicit IUserLayoutCustomizationShimScopedObject(Core::System& system_) + : ServiceFramework{system_, "nx2:chat:IUserLayoutCustomizationShimScopedObject"} { + static const FunctionInfo functions[] = { + {0, &IUserLayoutCustomizationShimScopedObject::StubReplyZeroed, "Initialize"}, + {1, &IUserLayoutCustomizationShimScopedObject::StubReplyEvent, "GetEvent (guessed)"}, + {12, &IUserLayoutCustomizationShimScopedObject::StubReplyZeroed, "EndFullScreenWithLocalCameraAvailableSection"}, + }; + RegisterHandlers(functions); } - - KernelHelpers::ServiceContext service_context; - Kernel::KEvent* event; }; -// Switch 2 GameChat (chat:u). Command 0 opens a session the title keeps using, so it has to -// return an interface; everything else falls through to the nx2 auto stub. +// Switch 2 GameChat (chat:u, nn::chat::IUserService). 0 and 1 open the session the title keeps +// using, 2 the layout customization object. 3 and 4 return objects nothing here implements yet +// (virtual camera, selective talk), so each gets an nx2 stub whose commands all succeed empty; MKW +// calls 3 at boot. Switchbrew leaves 0-4 unnamed: 2-4 are named after what they return. The named +// settings commands answer 0, which is what a console outside a GameChat room reports; everything +// else falls through to the nx2 auto stub. class IChatService final : public ServiceFramework { public: explicit IChatService(Core::System& system_) : ServiceFramework{system_, "nx2:chat:u"} { static const FunctionInfo functions[] = { {0, &IChatService::OpenSession, "OpenSession"}, + {1, &IChatService::OpenSession, "OpenSession (u32)"}, + {2, &IChatService::OpenUserLayoutCustomizationShimScopedObject, "OpenUserLayoutCustomizationShimScopedObject"}, + {3, &IChatService::OpenStubObject, "OpenVirtualCameraShimScopedObject"}, + {4, &IChatService::OpenStubObject, "OpenUserSelectiveTalkShimScopedObject"}, + {11, &IChatService::GetRoomInfo, "GetRoomInfo"}, + {14, &IChatService::StubReplyZeroed, "GetLocalCameraImageProcessingMode"}, + {15, &IChatService::StubReplyZeroed, "SetLocalCameraZoomMagnification"}, + {16, &IChatService::StubReplyZeroed, "GetLocalCameraZoomMagnification"}, + {21, &IChatService::StubReplyZeroed, "SetScreenSharingDisallowList"}, + {22, &IChatService::StubReplyZeroed, "ClearScreenSharingDisallowList"}, + {23, &IChatService::StubReplyZeroed, "SetScreenSharingMode"}, + {40, &IChatService::StubReplyZeroed, "GetRealtimeAudioLevels"}, + {51, &IChatService::StubReplyZeroed, "CalculateRequiredAudioStreamWorkBufferSize"}, + {60, &IChatService::StubReplyZeroed, "SetSharingKey"}, + {61, &IChatService::StubReplyZeroed, "ClearSharingKey"}, }; RegisterHandlers(functions); } private: void OpenSession(HLERequestContext& ctx) { - LOG_WARNING(Service_Friend, "(STUBBED) chat:u 0, returning a chat session"); + LOG_WARNING(Service_Friend, "(STUBBED) chat:u {}, returning a chat session", ctx.GetCommand()); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(ctx, system); } + + void OpenUserLayoutCustomizationShimScopedObject(HLERequestContext& ctx) { + LOG_DEBUG(Service_Friend, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(ctx, system); + } + + // 11: the nn::chat::RoomInfo (0xF0 bytes) of the room the user chats in. Nobody is ever in a + // room here, so it is all zeroes, ChattingUid (0x18) included. The auto stub left the title's + // buffer as it was. + void GetRoomInfo(HLERequestContext& ctx) { + LOG_DEBUG(Service_Friend, "called"); + constexpr std::array room_info{}; + ctx.WriteBuffer(room_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void OpenStubObject(HLERequestContext& ctx) { + static constexpr std::array names{ + "nx2:chat:IVirtualCameraShimScopedObject", + "nx2:chat:IUserSelectiveTalkShimScopedObject", + }; + const char* name = names[ctx.GetCommand() - 3]; + LOG_WARNING(Service_Friend, "(STUBBED) chat:u {}, returning {}", ctx.GetCommand(), name); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(ctx, system, name); + } }; void LoopProcess(Core::System& system) { diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index f2a7b7d04a..7830927459 100644 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "common/common_types.h" #include "common/logging.h" @@ -120,13 +121,13 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr r {134, C<&IHidServer::SetNpadAnalogStickUseCenterClamp>, "SetNpadAnalogStickUseCenterClamp"}, {135, C<&IHidServer::SetNpadCaptureButtonAssignment>, "SetNpadCaptureButtonAssignment"}, {136, C<&IHidServer::ClearNpadCaptureButtonAssignment>, "ClearNpadCaptureButtonAssignment"}, - {137, &IHidServer::StubReplyZeroed, "Unknown137"}, - {138, &IHidServer::StubReplyZeroed, "Unknown138"}, - {139, &IHidServer::StubReplyZeroed, "Unknown139"}, - {140, &IHidServer::StubReplyZeroed, "Unknown140"}, - {150, &IHidServer::StubReplyZeroed, "Unknown150"}, - {151, &IHidServer::StubReplyZeroed, "Unknown151"}, - {160, &IHidServer::StubReplyZeroed, "Unknown160"}, + {137, &IHidServer::StubReplyZeroed, "SetNpadGcAnalogStick8bitRawValue"}, + {138, C<&IHidServer::SetSceneIdForPrivateApp>, "SetSceneIdForPrivateApp"}, + {139, C<&IHidServer::GetSceneIdForPrivateApp>, "GetSceneIdForPrivateApp"}, + {140, &IHidServer::StubReplyZeroed, "IsFirmwareUpdateAvailableForDebug"}, + {150, &IHidServer::StubReplyZeroed, "GetSixAxisSensorIcSettings"}, + {151, &IHidServer::StubReplyZeroed, "SetSixAxisSensorIcSettings"}, + {160, &IHidServer::StubReplyZeroed, "GetSixAxisSensorIcAccelerationOffset"}, {200, C<&IHidServer::GetVibrationDeviceInfo>, "GetVibrationDeviceInfo"}, {201, C<&IHidServer::SendVibrationValue>, "SendVibrationValue"}, {202, C<&IHidServer::GetActualVibrationValue>, "GetActualVibrationValue"}, @@ -140,9 +141,9 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr r {210, C<&IHidServer::EndPermitVibrationSession>, "EndPermitVibrationSession"}, {211, C<&IHidServer::IsVibrationDeviceMounted>, "IsVibrationDeviceMounted"}, {212, C<&IHidServer::SendVibrationValueInBool>, "SendVibrationValueInBool"}, - {213, C<&IHidServer::Unknown213>, "Unknown213"}, - {214, C<&IHidServer::SendNx2VibrationValues>, "SendNx2VibrationValues (guessed)"}, - {220, C<&IHidServer::Unknown220>, "Unknown220"}, + {213, &IHidServer::SendVibrationValueInMode, "SendVibrationValueInMode"}, + {214, C<&IHidServer::SendVibrationValuesInMode>, "SendVibrationValuesInMode"}, + {220, C<&IHidServer::IsOunceVibrationDeviceMounted>, "IsOunceVibrationDeviceMounted"}, {300, C<&IHidServer::ActivateConsoleSixAxisSensor>, "ActivateConsoleSixAxisSensor"}, {301, C<&IHidServer::StartConsoleSixAxisSensor>, "StartConsoleSixAxisSensor"}, {302, C<&IHidServer::StopConsoleSixAxisSensor>, "StopConsoleSixAxisSensor"}, @@ -154,13 +155,13 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr r {308, &IHidServer::StubReplyZeroed, "SetSevenSixAxisSensorFusionStrength"}, {309, &IHidServer::StubReplyZeroed, "GetSevenSixAxisSensorFusionStrength"}, {310, C<&IHidServer::ResetSevenSixAxisSensorTimestamp>, "ResetSevenSixAxisSensorTimestamp"}, - {311, &IHidServer::StubReplyZeroed, "Unknown311"}, - {312, &IHidServer::StubReplyZeroed, "Unknown312"}, + {311, &IHidServer::StubReplyZeroed, "ForceActivateConsoleSixAxisSensor"}, + {312, &IHidServer::StubReplyZeroed, "ForceDeactivateConsoleSixAxisSensor"}, {320, &IHidServer::StubReplyZeroed, "EnableNpadImu"}, //21.0.0+ {321, &IHidServer::StubReplyZeroed, "DisableNpadImu"}, //21.0.0+ - {322, &IHidServer::StubReplyZeroed, "Unknown322"}, - {323, &IHidServer::StubReplyZeroed, "Unknown323"}, - {324, &IHidServer::StubReplyZeroed, "Unknown324"}, + {322, &IHidServer::StubReplyZeroed, "SetNpadImuSupportedFeatures"}, + {323, &IHidServer::StubReplyZeroed, "EnableNpadImuHandle"}, + {324, &IHidServer::StubReplyZeroed, "DisableNpadImuHandle"}, {400, C<&IHidServer::IsUsbFullKeyControllerEnabled>, "IsUsbFullKeyControllerEnabled"}, {401, &IHidServer::StubReplyZeroed, "EnableUsbFullKeyController"}, {402, &IHidServer::StubReplyZeroed, "IsUsbFullKeyControllerConnected"}, @@ -200,13 +201,13 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr r {527, C<&IHidServer::EnablePalmaBoostMode>, "EnablePalmaBoostMode"}, {528, C<&IHidServer::GetPalmaBluetoothAddress>, "GetPalmaBluetoothAddress"}, {529, C<&IHidServer::SetDisallowedPalmaConnection>, "SetDisallowedPalmaConnection"}, - {550, &IHidServer::StubReplyZeroed, "Unknown550"}, + {550, &IHidServer::StubReplyZeroed, "GetGlGrButtonConfigState"}, {1000, C<&IHidServer::SetNpadCommunicationMode>, "SetNpadCommunicationMode"}, {1001, C<&IHidServer::GetNpadCommunicationMode>, "GetNpadCommunicationMode"}, {1002, C<&IHidServer::SetTouchScreenConfiguration>, "SetTouchScreenConfiguration"}, {1003, C<&IHidServer::IsFirmwareUpdateNeededForNotification>, "IsFirmwareUpdateNeededForNotification"}, {1004, C<&IHidServer::SetTouchScreenResolution>, "SetTouchScreenResolution"}, - {1005, &IHidServer::StubReplyZeroed, "Unknown1005"}, + {1005, &IHidServer::StubReplyZeroed, "EnableNxTouchScreenEmulationForTouchEnter"}, {1270, nullptr, "DeleteButtonConfigStorageRight"}, {1271, nullptr, "IsUsingCustomButtonConfig"}, {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, @@ -255,24 +256,24 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr r {3013, &IHidServer::StubReplyZeroed, "SetDebugPadGenericPadMap"}, //21.0.0+ {3014, &IHidServer::StubReplyZeroed, "GetDebugPadKeyboardMap"}, //21.0.0+ {3015, &IHidServer::StubReplyZeroed, "SetDebugPadKeyboardMap"}, //21.0.0+ - {3110, &IHidServer::StubReplyZeroed, "Unknown3110"}, - {3111, &IHidServer::StubReplyZeroed, "Unknown3111"}, - {3112, &IHidServer::StubReplyZeroed, "Unknown3112"}, - {3113, &IHidServer::StubReplyZeroed, "Unknown3113"}, + {3110, &IHidServer::StubReplyZeroed, "InitializeJcms"}, + {3111, &IHidServer::StubReplyZeroed, "FinalizeJcms"}, + {3112, &IHidServer::StubReplyZeroed, "StartJcms"}, + {3113, &IHidServer::StubReplyZeroed, "StopJcms"}, {3114, &IHidServer::StubReplyZeroed, "Unknown3114"}, - {3130, &IHidServer::StubReplyZeroed, "Unknown3130"}, - {3131, &IHidServer::StubReplyZeroed, "Unknown3131"}, - {3132, &IHidServer::StubReplyZeroed, "Unknown3132"}, - {3133, &IHidServer::StubReplyZeroed, "Unknown3133"}, + {3130, &IHidServer::StubReplyZeroed, "InitializeNmouse"}, + {3131, &IHidServer::StubReplyZeroed, "FinalizeNmouse"}, + {3132, C<&IHidServer::SetNmousePointerRanges>, "SetNmousePointerRanges"}, + {3133, C<&IHidServer::GetNmousePointerRanges>, "GetNmousePointerRanges"}, {3134, &IHidServer::StubReplyZeroed, "Unknown3134"}, - {3135, &IHidServer::StubReplyZeroed, "Unknown3135"}, - {3136, &IHidServer::StubReplyZeroed, "Unknown3136"}, - {3137, &IHidServer::StubReplyZeroed, "Unknown3137"}, - {3138, &IHidServer::StubReplyZeroed, "Unknown3138"}, - {3139, &IHidServer::StubReplyZeroed, "Unknown3139"}, - {3140, &IHidServer::StubReplyZeroed, "Unknown3140"}, - {3141, &IHidServer::StubReplyZeroed, "Unknown3141"}, - {3142, &IHidServer::StubReplyZeroed, "Unknown3142"}, + {3135, &IHidServer::StubReplyZeroed, "EnableShowingCursor"}, + {3136, &IHidServer::StubReplyZeroed, "SetNmouseButtonConfigJoyLeftJcms"}, + {3137, &IHidServer::StubReplyZeroed, "SetNmouseButtonConfigJoyRightJcms"}, + {3138, &IHidServer::StubReplyZeroed, "SetMergedNmouseSourceTypes"}, + {3139, C<&IHidServer::InitializeNmouse2>, "InitializeNmouse2"}, + {3140, &IHidServer::StubReplyZeroed, "SetNmousePointerPosition"}, + {3141, &IHidServer::StubReplyZeroed, "EnableNmousePointer"}, + {3142, &IHidServer::StubReplyZeroed, "RestrictNmousePointer"}, {3143, &IHidServer::StubReplyZeroed, "Unknown3143"}, {3144, &IHidServer::StubReplyZeroed, "Unknown3144"}, {3145, &IHidServer::StubReplyZeroed, "Unknown3145"}, @@ -1113,7 +1114,7 @@ Result IHidServer::IsVibrationDeviceMounted( R_SUCCEED(); } -Result IHidServer::SendNx2VibrationValues( +Result IHidServer::SendVibrationValuesInMode( u64 unknown, AppletResourceUserId aruid, InArray vibration_handles, InArray vibration_values) { @@ -1134,14 +1135,84 @@ Result IHidServer::SendNx2VibrationValues( R_SUCCEED(); } -Result IHidServer::Unknown213() { - LOG_DEBUG(Service_HID, "(STUBBED) called"); +void IHidServer::SendVibrationValueInMode(HLERequestContext& ctx) { + // Switchbrew names 213 but gives no layout, and two fit its 32 bytes (the mode before the + // handle, or after the value as in 206's padding). Log the first request so a run shows which. + static std::once_flag logged; + std::call_once(logged, [&ctx] { + IPC::RequestParser rp{ctx}; + const auto w = rp.PopRaw>(); + LOG_INFO(Service_HID, + "(STUBBED) first request: {:08x} {:08x} {:08x} {:08x} {:08x} {:08x} {:08x} {:08x}", + w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]); + }); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +Result IHidServer::IsOunceVibrationDeviceMounted( + Out out_is_mounted, Core::HID::VibrationDeviceHandle vibration_device_handle, + ClientAppletResourceUserId aruid) { + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + vibration_device_handle.npad_type, vibration_device_handle.npad_id, + vibration_device_handle.device_index, aruid.pid); + + // Like IsVibrationDeviceMounted, but a handle this doesn't recognise reads as not mounted + // instead of failing: the Switch 2 handle layout is only known from captures (see 214). + *out_is_mounted = false; + if (IsVibrationHandleValid(vibration_device_handle).IsSuccess()) { + const NpadVibrationBase* device = + GetResourceManager()->GetVibrationDevice(vibration_device_handle); + if (device != nullptr) { + *out_is_mounted = device->IsVibrationMounted(); + } + } + + R_SUCCEED(); +} + +Result IHidServer::SetSceneIdForPrivateApp(ClientAppletResourceUserId aruid, u64 scene_id) { + LOG_DEBUG(Service_HID, "called, scene_id={:#x}, applet_resource_user_id={}", scene_id, aruid.pid); + scene_ids_for_private_app[aruid.pid] = scene_id; + R_SUCCEED(); +} + +Result IHidServer::GetSceneIdForPrivateApp(Out out_scene_id, + ClientAppletResourceUserId aruid) { + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", aruid.pid); + const auto it = scene_ids_for_private_app.find(aruid.pid); + *out_scene_id = it != scene_ids_for_private_app.end() ? it->second : 0; + R_SUCCEED(); +} + +Result IHidServer::InitializeNmouse2(u32 pointer_type, s32 width, s32 height, u32 unknown, + ClientAppletResourceUserId aruid, u64 t_mem_size, + InCopyHandle t_mem) { + LOG_WARNING(Service_HID, + "(STUBBED) called, pointer_type={}, width={}, height={}, unknown={:#x}, " + "applet_resource_user_id={}, t_mem_size={:#x}", + pointer_type, width, height, unknown, aruid.pid, t_mem_size); + nmouse_pointer_ranges[aruid.pid] = {width, height}; R_SUCCEED(); } -Result IHidServer::Unknown220(Out out) { - LOG_DEBUG(Service_HID, "(STUBBED) called"); - *out = 0; +Result IHidServer::SetNmousePointerRanges(s32 width, s32 height, + ClientAppletResourceUserId aruid) { + LOG_DEBUG(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, + height, aruid.pid); + nmouse_pointer_ranges[aruid.pid] = {width, height}; + R_SUCCEED(); +} + +Result IHidServer::GetNmousePointerRanges(Out out_width, Out out_height, + ClientAppletResourceUserId aruid) { + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", aruid.pid); + const auto it = nmouse_pointer_ranges.find(aruid.pid); + const NmousePointerRanges ranges = + it != nmouse_pointer_ranges.end() ? it->second : NmousePointerRanges{}; + *out_width = ranges.width; + *out_height = ranges.height; R_SUCCEED(); } diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h index 9bb62fabc4..7b65ca9b35 100644 --- a/src/core/hle/service/hid/hid_server.h +++ b/src/core/hle/service/hid/hid_server.h @@ -6,6 +6,8 @@ #pragma once +#include + #include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" #include "hid_core/hid_types.h" @@ -191,16 +193,33 @@ class IHidServer final : public ServiceFramework { ClientAppletResourceUserId aruid); // Switch 2 sends vibration every frame with 214: pointer buffers of device handles and values // like 206, but the aruid is raw data after an unknown u64 instead of a sent process id. - Result SendNx2VibrationValues( + Result SendVibrationValuesInMode( u64 unknown, AppletResourceUserId aruid, InArray vibration_handles, InArray vibration_values); // 213 (32 bytes, aruid): sent per controller every frame by Kirby Air Riders (tens of - // thousands of auto stub log lines a run); no reply data. Accepted and dropped. - Result Unknown213(); - // 220 (16 bytes, aruid): replies one byte (meaning unknown); answer 0 instead of leaving it to - // the message buffer's leftovers. - Result Unknown220(Out out); + // thousands of auto stub log lines a run); no reply data. Accepted and dropped until its + // layout is known: the first request's raw words are logged for that. + void SendVibrationValueInMode(HLERequestContext& ctx); + // 220: whether the Switch 2 vibration device behind a handle is mounted, as 211 answers it for + // Switch 1 devices. + Result IsOunceVibrationDeviceMounted(Out out_is_mounted, + Core::HID::VibrationDeviceHandle vibration_device_handle, + ClientAppletResourceUserId aruid); + // 138/139 (Switch 2): a scene id each application stores and reads back. + Result SetSceneIdForPrivateApp(ClientAppletResourceUserId aruid, u64 scene_id); + Result GetSceneIdForPrivateApp(Out out_scene_id, ClientAppletResourceUserId aruid); + // Switch 2 mouse mode (Nmouse). The pointer ranges (the screen size the pointer moves in) an + // application sets with InitializeNmouse2 or SetNmousePointerRanges are what + // GetNmousePointerRanges reads back. No mouse is ever attached, so the shared memory the title + // hands over stays as it is. InitializeNmouse (3130) stays a placeholder: no title has been + // seen calling it, so its layout is unconfirmed. + Result InitializeNmouse2(u32 pointer_type, s32 width, s32 height, u32 unknown, + ClientAppletResourceUserId aruid, u64 t_mem_size, + InCopyHandle t_mem); + Result SetNmousePointerRanges(s32 width, s32 height, ClientAppletResourceUserId aruid); + Result GetNmousePointerRanges(Out out_width, Out out_height, + ClientAppletResourceUserId aruid); Result ActivateConsoleSixAxisSensor(ClientAppletResourceUserId aruid); Result StartConsoleSixAxisSensor(Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle, ClientAppletResourceUserId aruid); @@ -279,6 +298,13 @@ class IHidServer final : public ServiceFramework { std::shared_ptr resource_manager; std::shared_ptr firmware_settings; + struct NmousePointerRanges { + s32 width; + s32 height; + }; + // Keyed by applet resource user id, as the console keeps them per application. + std::unordered_map scene_ids_for_private_app; + std::unordered_map nmouse_pointer_ranges; }; } // namespace Service::HID 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 b6aa71dda6..8cd9b16f7e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -86,7 +86,7 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span i } case 0xb: if (g_nx2_driver) { - return WrapFixed(this, &nvhost_as_gpu::Nx2GetSyncpointShimInfo, input, output); + return WrapFixed(this, &nvhost_as_gpu::GetSyncRoMap, input, output); } break; case 0x14: @@ -308,10 +308,11 @@ void nvhost_as_gpu::Nx2SparseTile(u64 gpu_address, u64 cpu_address, u64 size) { void nvhost_as_gpu::OnClose(DeviceFD fd) {} -// Switch 2 NvRm (with characteristics flag 26) asks for the read-only syncpoint shim: a GPU page -// whose u32 at id * stride reads as that syncpoint's value; NVN waits on syncpoints through it and -// asserts without one. Same scheme as changes.patch, placed on the last page of the VA range. -NvResult nvhost_as_gpu::Nx2GetSyncpointShimInfo(IoctlNx2SyncpointShimInfo& params) { +// NVGPU_AS_IOCTL_GET_SYNC_RO_MAP: Switch 2 NvRm (with characteristics flag 26) asks for the +// read-only syncpoint shim, a GPU page whose u32 at id * sync_size reads as that syncpoint's +// value; NVN waits on syncpoints through it and asserts without one. Same scheme as +// changes.patch, placed on the last page of the VA range. +NvResult nvhost_as_gpu::GetSyncRoMap(IoctlGetSyncRoMap& params) { std::scoped_lock lock(mutex); if (!vm.initialised || !gmmu) { return NvResult::NotSupported; @@ -321,11 +322,11 @@ NvResult nvhost_as_gpu::Nx2GetSyncpointShimInfo(IoctlNx2SyncpointShimInfo& param nx2_syncpoint_shim = true; gmmu->SetNativeSyncpointShim(address); } - params.address = address; - params.stride = sizeof(u32); - params.count = Tegra::MemoryManager::NumShimSyncpoints; + params.base_gpuva = address; + params.sync_size = sizeof(u32); + params.num_syncpoints = Tegra::MemoryManager::NumShimSyncpoints; LOG_INFO(Service_NVDRV, "Switch 2 syncpoint shim at {:#x} ({} syncpoints)", address, - params.count); + params.num_syncpoints); return NvResult::Success; } 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 37b6d0d4c4..786c37c822 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -222,13 +222,13 @@ class nvhost_as_gpu final : public nvdevice { } vm; std::unique_ptr gmmu; - struct IoctlNx2SyncpointShimInfo { - u64_le address; - u32_le stride; - u32_le count; + struct IoctlGetSyncRoMap { + u64_le base_gpuva; + u32_le sync_size; + u32_le num_syncpoints; }; - static_assert(sizeof(IoctlNx2SyncpointShimInfo) == 16); - NvResult Nx2GetSyncpointShimInfo(IoctlNx2SyncpointShimInfo& params); + static_assert(sizeof(IoctlGetSyncRoMap) == 16); + NvResult GetSyncRoMap(IoctlGetSyncRoMap& params); bool nx2_syncpoint_shim{}; // Switch 2: maps GPU-accessible process memory (CPU range) at a GPU address.