diff options
Diffstat (limited to 'src/core/hle')
51 files changed, 2235 insertions, 1187 deletions
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 45135a07f..5b3feec66 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -287,18 +287,52 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, BufferDescriptorB().size() > buffer_index && BufferDescriptorB()[buffer_index].Size() >= size, { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size); - memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); + WriteBufferB(buffer, size, buffer_index); } else { ASSERT_OR_EXECUTE_MSG( BufferDescriptorC().size() > buffer_index && BufferDescriptorC()[buffer_index].Size() >= size, { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size); - memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); + WriteBufferC(buffer, size, buffer_index); } return size; } +std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size, + std::size_t buffer_index) const { + if (buffer_index >= BufferDescriptorB().size() || size == 0) { + return 0; + } + + const auto buffer_size{BufferDescriptorB()[buffer_index].Size()}; + if (size > buffer_size) { + LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, + buffer_size); + size = buffer_size; // TODO(bunnei): This needs to be HW tested + } + + memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); + return size; +} + +std::size_t HLERequestContext::WriteBufferC(const void* buffer, std::size_t size, + std::size_t buffer_index) const { + if (buffer_index >= BufferDescriptorC().size() || size == 0) { + return 0; + } + + const auto buffer_size{BufferDescriptorC()[buffer_index].Size()}; + if (size > buffer_size) { + LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, + buffer_size); + size = buffer_size; // TODO(bunnei): This needs to be HW tested + } + + memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); + return size; +} + std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const { const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && BufferDescriptorA()[buffer_index].Size()}; diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index d3abeee85..99265ce90 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -277,6 +277,14 @@ public: std::size_t WriteBuffer(const void* buffer, std::size_t size, std::size_t buffer_index = 0) const; + /// Helper function to write buffer B + std::size_t WriteBufferB(const void* buffer, std::size_t size, + std::size_t buffer_index = 0) const; + + /// Helper function to write buffer C + std::size_t WriteBufferC(const void* buffer, std::size_t size, + std::size_t buffer_index = 0) const; + /* Helper function to write a buffer using the appropriate buffer descriptor * * @tparam T an arbitrary container that satisfies the diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h index 2410f74a3..2e7e1436a 100644 --- a/src/core/hle/kernel/k_code_memory.h +++ b/src/core/hle/kernel/k_code_memory.h @@ -30,19 +30,19 @@ public: explicit KCodeMemory(KernelCore& kernel_); Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); - void Finalize(); + void Finalize() override; Result Map(VAddr address, size_t size); Result Unmap(VAddr address, size_t size); Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); Result UnmapFromOwner(VAddr address, size_t size); - bool IsInitialized() const { + bool IsInitialized() const override { return m_is_initialized; } static void PostDestroy([[maybe_unused]] uintptr_t arg) {} - KProcess* GetOwner() const { + KProcess* GetOwner() const override { return m_owner; } VAddr GetSourceAddress() const { diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 926c6dc84..f4072e1c3 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -93,19 +93,7 @@ struct KernelCore::Impl { process_list.clear(); - // Close all open server sessions and ports. - std::unordered_set<KAutoObject*> server_objects_; - { - std::scoped_lock lk(server_objects_lock); - server_objects_ = server_objects; - server_objects.clear(); - } - for (auto* server_object : server_objects_) { - server_object->Close(); - } - - // Ensures all service threads gracefully shutdown. - ClearServiceThreads(); + CloseServices(); next_object_id = 0; next_kernel_process_id = KProcess::InitialKIPIDMin; @@ -188,6 +176,22 @@ struct KernelCore::Impl { global_object_list_container.reset(); } + void CloseServices() { + // Close all open server sessions and ports. + std::unordered_set<KAutoObject*> server_objects_; + { + std::scoped_lock lk(server_objects_lock); + server_objects_ = server_objects; + server_objects.clear(); + } + for (auto* server_object : server_objects_) { + server_object->Close(); + } + + // Ensures all service threads gracefully shutdown. + ClearServiceThreads(); + } + void InitializePhysicalCores() { exclusive_monitor = Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); @@ -817,6 +821,10 @@ void KernelCore::Shutdown() { impl->Shutdown(); } +void KernelCore::CloseServices() { + impl->CloseServices(); +} + const KResourceLimit* KernelCore::GetSystemResourceLimit() const { return impl->system_resource_limit; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index aa0ebaa02..6c7cf6af2 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -109,6 +109,9 @@ public: /// Clears all resources in use by the kernel instance. void Shutdown(); + /// Close all active services in use by the kernel instance. + void CloseServices(); + /// Retrieves a shared pointer to the system resource limit instance. const KResourceLimit* GetSystemResourceLimit() const; diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index b726ac27a..def105832 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -290,7 +290,7 @@ protected: void Get(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString()); ProfileBase profile_base{}; - ProfileData data{}; + UserData data{}; if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { ctx.WriteBuffer(data); IPC::ResponseBuilder rb{ctx, 16}; @@ -373,18 +373,18 @@ protected: reinterpret_cast<const char*>(base.username.data()), base.username.size()), base.timestamp, base.user_uuid.RawString()); - if (user_data.size() < sizeof(ProfileData)) { - LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); + if (user_data.size() < sizeof(UserData)) { + LOG_ERROR(Service_ACC, "UserData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_INVALID_BUFFER); return; } - ProfileData data; - std::memcpy(&data, user_data.data(), sizeof(ProfileData)); + UserData data; + std::memcpy(&data, user_data.data(), sizeof(UserData)); if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) { - LOG_ERROR(Service_ACC, "Failed to update profile data and base!"); + LOG_ERROR(Service_ACC, "Failed to update user data and base!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_FAILED_SAVE_DATA); return; @@ -406,15 +406,15 @@ protected: reinterpret_cast<const char*>(base.username.data()), base.username.size()), base.timestamp, base.user_uuid.RawString()); - if (user_data.size() < sizeof(ProfileData)) { - LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); + if (user_data.size() < sizeof(UserData)) { + LOG_ERROR(Service_ACC, "UserData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_INVALID_BUFFER); return; } - ProfileData data; - std::memcpy(&data, user_data.data(), sizeof(ProfileData)); + UserData data; + std::memcpy(&data, user_data.data(), sizeof(UserData)); Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Write, Common::FS::FileType::BinaryFile); diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 8118ead33..a58da4d5f 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -22,7 +22,7 @@ struct UserRaw { UUID uuid2{}; u64 timestamp{}; ProfileUsername username{}; - ProfileData extra_data{}; + UserData extra_data{}; }; static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size."); @@ -263,7 +263,7 @@ UUID ProfileManager::GetLastOpenedUser() const { /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, - ProfileData& data) const { + UserData& data) const { if (GetProfileBase(index, profile)) { data = profiles[*index].data; return true; @@ -272,15 +272,14 @@ bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, Pro } /// Return the users profile base and the unknown arbitary data. -bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, - ProfileData& data) const { +bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, UserData& data) const { const auto idx = GetUserIndex(uuid); return GetProfileBaseAndData(idx, profile, data); } /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, - ProfileData& data) const { + UserData& data) const { return GetProfileBaseAndData(user.user_uuid, profile, data); } @@ -318,7 +317,7 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { } bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, - const ProfileData& data_new) { + const UserData& data_new) { const auto index = GetUserIndex(uuid); if (index.has_value() && SetProfileBase(uuid, profile_new)) { profiles[*index].data = data_new; diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 9940957f1..135f7d0d5 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -22,7 +22,7 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>; /// Contains extra data related to a user. /// TODO: RE this structure -struct ProfileData { +struct UserData { INSERT_PADDING_WORDS_NOINIT(1); u32 icon_id; u8 bg_color_id; @@ -30,7 +30,7 @@ struct ProfileData { INSERT_PADDING_BYTES_NOINIT(0x10); INSERT_PADDING_BYTES_NOINIT(0x60); }; -static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size"); +static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); /// This holds general information about a users profile. This is where we store all the information /// based on a specific user @@ -38,7 +38,7 @@ struct ProfileInfo { Common::UUID user_uuid{}; ProfileUsername username{}; u64 creation_time{}; - ProfileData data{}; // TODO(ognik): Work out what this is + UserData data{}; // TODO(ognik): Work out what this is bool is_open{}; }; @@ -74,10 +74,9 @@ public: bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const; bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, - ProfileData& data) const; - bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const; - bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, - ProfileData& data) const; + UserData& data) const; + bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, UserData& data) const; + bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, UserData& data) const; std::size_t GetUserCount() const; std::size_t GetOpenUserCount() const; bool UserExists(Common::UUID uuid) const; @@ -93,7 +92,7 @@ public: bool RemoveUser(Common::UUID uuid); bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new); bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, - const ProfileData& data_new); + const UserData& data_new); private: void ParseUserSaveFile(); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index d35644e73..118f226e4 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -5,7 +5,6 @@ #include <array> #include <cinttypes> #include <cstring> -#include "audio_core/audio_renderer.h" #include "common/settings.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" @@ -238,6 +237,7 @@ IDebugFunctions::IDebugFunctions(Core::System& system_) {130, nullptr, "FriendInvitationSetApplicationParameter"}, {131, nullptr, "FriendInvitationClearApplicationParameter"}, {132, nullptr, "FriendInvitationPushApplicationParameter"}, + {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"}, {900, nullptr, "GetGrcProcessLaunchedSystemEvent"}, }; // clang-format on @@ -285,7 +285,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, {64, nullptr, "SetInputDetectionSourceSet"}, - {65, nullptr, "ReportUserIsActive"}, + {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"}, {66, nullptr, "GetCurrentIlluminance"}, {67, nullptr, "IsIlluminanceAvailable"}, {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"}, @@ -517,6 +517,13 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c rb.Push<u32>(idle_time_detection_extension); } +void ISelfController::ReportUserIsActive(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; is_auto_sleep_disabled = rp.Pop<bool>(); @@ -635,6 +642,10 @@ void AppletMessageQueue::RequestExit() { PushMessage(AppletMessage::Exit); } +void AppletMessageQueue::RequestResume() { + PushMessage(AppletMessage::Resume); +} + void AppletMessageQueue::FocusStateChanged() { PushMessage(AppletMessage::FocusStateChanged); } @@ -1310,6 +1321,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, {34, nullptr, "SelectApplicationLicense"}, {35, nullptr, "GetDeviceSaveDataSizeMax"}, + {36, nullptr, "GetLimitedApplicationLicense"}, + {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"}, {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, {60, nullptr, "SetMediaPlaybackStateForApplication"}, diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 988ead215..bb75c6281 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -90,6 +90,7 @@ public: AppletMessage PopMessage(); std::size_t GetMessageCount() const; void RequestExit(); + void RequestResume(); void FocusStateChanged(); void OperationModeChanged(); @@ -174,6 +175,7 @@ private: void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); + void ReportUserIsActive(Kernel::HLERequestContext& ctx); void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx); void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx); void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 18d3ae682..48a9a73a0 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -1,68 +1,211 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "audio_core/in/audio_in_system.h" +#include "audio_core/renderer/audio_device.h" +#include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/string_util.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/audio/audin_u.h" namespace Service::Audio { +using namespace AudioCore::AudioIn; -IAudioIn::IAudioIn(Core::System& system_) - : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetAudioInState"}, - {1, &IAudioIn::Start, "Start"}, - {2, nullptr, "Stop"}, - {3, nullptr, "AppendAudioInBuffer"}, - {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"}, - {5, nullptr, "GetReleasedAudioInBuffer"}, - {6, nullptr, "ContainsAudioInBuffer"}, - {7, nullptr, "AppendUacInBuffer"}, - {8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"}, - {9, nullptr, "GetReleasedAudioInBuffersAuto"}, - {10, nullptr, "AppendUacInBufferAuto"}, - {11, nullptr, "GetAudioInBufferCount"}, - {12, nullptr, "SetDeviceGain"}, - {13, nullptr, "GetDeviceGain"}, - {14, nullptr, "FlushAudioInBuffers"}, - }; - // clang-format on +class IAudioIn final : public ServiceFramework<IAudioIn> { +public: + explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, + std::string& device_name, const AudioInParameter& in_params, u32 handle, + u64 applet_resource_user_id) + : ServiceFramework{system_, "IAudioIn"}, + service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, + impl{std::make_shared<In>(system_, manager, event, session_id)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IAudioIn::GetAudioInState, "GetAudioInState"}, + {1, &IAudioIn::Start, "Start"}, + {2, &IAudioIn::Stop, "Stop"}, + {3, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBuffer"}, + {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"}, + {5, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffer"}, + {6, &IAudioIn::ContainsAudioInBuffer, "ContainsAudioInBuffer"}, + {7, &IAudioIn::AppendAudioInBuffer, "AppendUacInBuffer"}, + {8, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBufferAuto"}, + {9, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffersAuto"}, + {10, &IAudioIn::AppendAudioInBuffer, "AppendUacInBufferAuto"}, + {11, &IAudioIn::GetAudioInBufferCount, "GetAudioInBufferCount"}, + {12, &IAudioIn::SetDeviceGain, "SetDeviceGain"}, + {13, &IAudioIn::GetDeviceGain, "GetDeviceGain"}, + {14, &IAudioIn::FlushAudioInBuffers, "FlushAudioInBuffers"}, + }; + // clang-format on - RegisterHandlers(functions); + RegisterHandlers(functions); - buffer_event = service_context.CreateEvent("IAudioIn:BufferEvent"); -} + if (impl->GetSystem() + .Initialize(device_name, in_params, handle, applet_resource_user_id) + .IsError()) { + LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!"); + } + } -IAudioIn::~IAudioIn() { - service_context.CloseEvent(buffer_event); -} + ~IAudioIn() override { + impl->Free(); + service_context.CloseEvent(event); + } -void IAudioIn::Start(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + [[nodiscard]] std::shared_ptr<In> GetImpl() { + return impl; + } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} +private: + void GetAudioInState(Kernel::HLERequestContext& ctx) { + const auto state = static_cast<u32>(impl->GetState()); -void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called. State={}", state); - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); -} + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(state); + } -void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + void Start(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} + auto result = impl->StartSystem(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void Stop(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + auto result = impl->StopSystem(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void AppendAudioInBuffer(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u64 tag = rp.PopRaw<u64>(); -AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} { + const auto in_buffer_size{ctx.GetReadBufferSize()}; + if (in_buffer_size < sizeof(AudioInBuffer)) { + LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!"); + } + + const auto& in_buffer = ctx.ReadBuffer(); + AudioInBuffer buffer{}; + std::memcpy(&buffer, in_buffer.data(), sizeof(AudioInBuffer)); + + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag); + + auto result = impl->AppendBuffer(buffer, tag); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + auto& buffer_event = impl->GetBufferEvent(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(buffer_event); + } + + void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) { + auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); + std::vector<u64> released_buffers(write_buffer_size, 0); + + auto count = impl->GetReleasedBuffers(released_buffers); + + [[maybe_unused]] std::string tags{}; + for (u32 i = 0; i < count; i++) { + tags += fmt::format("{:08X}, ", released_buffers[i]); + } + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, + tags); + + ctx.WriteBuffer(released_buffers); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(count); + } + + void ContainsAudioInBuffer(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const u64 tag{rp.Pop<u64>()}; + const auto buffer_queued{impl->ContainsAudioBuffer(tag)}; + + LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(buffer_queued); + } + + void GetAudioInBufferCount(Kernel::HLERequestContext& ctx) { + const auto buffer_count = impl->GetBufferCount(); + + LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); + + IPC::ResponseBuilder rb{ctx, 3}; + + rb.Push(ResultSuccess); + rb.Push(buffer_count); + } + + void SetDeviceGain(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto volume{rp.Pop<f32>()}; + LOG_DEBUG(Service_Audio, "called. Gain {}", volume); + + impl->SetVolume(volume); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetDeviceGain(Kernel::HLERequestContext& ctx) { + auto volume{impl->GetVolume()}; + + LOG_DEBUG(Service_Audio, "called. Gain {}", volume); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(volume); + } + + void FlushAudioInBuffers(Kernel::HLERequestContext& ctx) { + bool flushed{impl->FlushAudioInBuffers()}; + + LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(flushed); + } + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* event; + std::shared_ptr<AudioCore::AudioIn::In> impl; +}; + +AudInU::AudInU(Core::System& system_) + : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew}, + service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>( + system_)} { // clang-format off static const FunctionInfo functions[] = { {0, &AudInU::ListAudioIns, "ListAudioIns"}, @@ -80,59 +223,152 @@ AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} { AudInU::~AudInU() = default; void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) { + using namespace AudioCore::AudioRenderer; + LOG_DEBUG(Service_Audio, "called"); - const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioInDeviceName); - const std::size_t device_count = std::min(count, audio_device_names.size()); - std::vector<AudioInDeviceName> device_names; - device_names.reserve(device_count); + const auto write_count = + static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + std::vector<AudioDevice::AudioDeviceName> device_names{}; - for (std::size_t i = 0; i < device_count; i++) { - const auto& device_name = audio_device_names[i]; - auto& entry = device_names.emplace_back(); - device_name.copy(entry.data(), device_name.size()); + u32 out_count{0}; + if (write_count > 0) { + out_count = impl->GetDeviceNames(device_names, write_count, false); + ctx.WriteBuffer(device_names); } - ctx.WriteBuffer(device_names); - IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(device_names.size())); + rb.Push(out_count); } void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) { + using namespace AudioCore::AudioRenderer; + LOG_DEBUG(Service_Audio, "called"); - constexpr u32 device_count = 0; - // Since we don't actually use any other audio input devices, we return 0 devices. Filtered - // device listing just omits the default input device + const auto write_count = + static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + std::vector<AudioDevice::AudioDeviceName> device_names{}; + + u32 out_count{0}; + if (write_count > 0) { + out_count = impl->GetDeviceNames(device_names, write_count, true); + ctx.WriteBuffer(device_names); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(device_count)); + rb.Push(out_count); } -void AudInU::OpenInOutImpl(Kernel::HLERequestContext& ctx) { - AudInOutParams params{}; - params.channel_count = 2; - params.sample_format = SampleFormat::PCM16; - params.sample_rate = 48000; - params.state = State::Started; +void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto in_params{rp.PopRaw<AudioInParameter>()}; + auto applet_resource_user_id{rp.PopRaw<u64>()}; + const auto device_name_data{ctx.ReadBuffer()}; + auto device_name = Common::StringFromBuffer(device_name_data); + auto handle{ctx.GetCopyHandle(0)}; + + std::scoped_lock l{impl->mutex}; + auto link{impl->LinkToManager()}; + if (link.IsError()) { + LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(link); + return; + } + + size_t new_session_id{}; + auto result{impl->AcquireSessionId(new_session_id)}; + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, + impl->num_free_sessions); + + auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, + in_params, handle, applet_resource_user_id); + impl->sessions[new_session_id] = audio_in->GetImpl(); + impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; + + auto& out_system = impl->sessions[new_session_id]->GetSystem(); + AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), + .channel_count = out_system.GetChannelCount(), + .sample_format = + static_cast<u32>(out_system.GetSampleFormat()), + .state = static_cast<u32>(out_system.GetState())}; IPC::ResponseBuilder rb{ctx, 6, 0, 1}; - rb.Push(ResultSuccess); - rb.PushRaw<AudInOutParams>(params); - rb.PushIpcInterface<IAudioIn>(system); -} -void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); - OpenInOutImpl(ctx); + std::string out_name{out_system.GetName()}; + ctx.WriteBuffer(out_name); + + rb.Push(ResultSuccess); + rb.PushRaw<AudioInParameterInternal>(out_params); + rb.PushIpcInterface<IAudioIn>(audio_in); } void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); - OpenInOutImpl(ctx); + IPC::RequestParser rp{ctx}; + auto protocol_specified{rp.PopRaw<u64>()}; + auto in_params{rp.PopRaw<AudioInParameter>()}; + auto applet_resource_user_id{rp.PopRaw<u64>()}; + const auto device_name_data{ctx.ReadBuffer()}; + auto device_name = Common::StringFromBuffer(device_name_data); + auto handle{ctx.GetCopyHandle(0)}; + + std::scoped_lock l{impl->mutex}; + auto link{impl->LinkToManager()}; + if (link.IsError()) { + LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(link); + return; + } + + size_t new_session_id{}; + auto result{impl->AcquireSessionId(new_session_id)}; + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, + impl->num_free_sessions); + + auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, + in_params, handle, applet_resource_user_id); + impl->sessions[new_session_id] = audio_in->GetImpl(); + impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; + + auto& out_system = impl->sessions[new_session_id]->GetSystem(); + AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), + .channel_count = out_system.GetChannelCount(), + .sample_format = + static_cast<u32>(out_system.GetSampleFormat()), + .state = static_cast<u32>(out_system.GetState())}; + + IPC::ResponseBuilder rb{ctx, 6, 0, 1}; + + std::string out_name{out_system.GetName()}; + if (protocol_specified == 0) { + if (out_system.IsUac()) { + out_name = "UacIn"; + } else { + out_name = "DeviceIn"; + } + } + + ctx.WriteBuffer(out_name); + + rb.Push(ResultSuccess); + rb.PushRaw<AudioInParameterInternal>(out_params); + rb.PushIpcInterface<IAudioIn>(audio_in); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h index 2bfa38ecc..b45fda78a 100644 --- a/src/core/hle/service/audio/audin_u.h +++ b/src/core/hle/service/audio/audin_u.h @@ -3,6 +3,8 @@ #pragma once +#include "audio_core/audio_in_manager.h" +#include "audio_core/in/audio_in.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" @@ -14,22 +16,12 @@ namespace Kernel { class HLERequestContext; } -namespace Service::Audio { - -class IAudioIn final : public ServiceFramework<IAudioIn> { -public: - explicit IAudioIn(Core::System& system_); - ~IAudioIn() override; - -private: - void Start(Kernel::HLERequestContext& ctx); - void RegisterBufferEvent(Kernel::HLERequestContext& ctx); - void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx); - - KernelHelpers::ServiceContext service_context; +namespace AudioCore::AudioOut { +class Manager; +class In; +} // namespace AudioCore::AudioOut - Kernel::KEvent* buffer_event; -}; +namespace Service::Audio { class AudInU final : public ServiceFramework<AudInU> { public: @@ -37,33 +29,14 @@ public: ~AudInU() override; private: - enum class SampleFormat : u32_le { - PCM16 = 2, - }; - - enum class State : u32_le { - Started = 0, - Stopped = 1, - }; - - struct AudInOutParams { - u32_le sample_rate{}; - u32_le channel_count{}; - SampleFormat sample_format{}; - State state{}; - }; - static_assert(sizeof(AudInOutParams) == 0x10, "AudInOutParams is an invalid size"); - - using AudioInDeviceName = std::array<char, 256>; - static constexpr std::array<std::string_view, 1> audio_device_names{{ - "BuiltInHeadset", - }}; - void ListAudioIns(Kernel::HLERequestContext& ctx); void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx); void OpenInOutImpl(Kernel::HLERequestContext& ctx); void OpenAudioIn(Kernel::HLERequestContext& ctx); void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx); + + KernelHelpers::ServiceContext service_context; + std::unique_ptr<AudioCore::AudioIn::Manager> impl; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index b0dad6053..a44dd842a 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -5,56 +5,43 @@ #include <cstring> #include <vector> -#include "audio_core/audio_out.h" -#include "audio_core/codec.h" +#include "audio_core/out/audio_out_system.h" +#include "audio_core/renderer/audio_device.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/string_util.h" #include "common/swap.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" #include "core/hle/service/audio/audout_u.h" #include "core/hle/service/audio/errors.h" -#include "core/hle/service/kernel_helpers.h" #include "core/memory.h" namespace Service::Audio { - -constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; -constexpr int DefaultSampleRate{48000}; - -struct AudoutParams { - s32_le sample_rate; - u16_le channel_count; - INSERT_PADDING_BYTES_NOINIT(2); -}; -static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size"); - -enum class AudioState : u32 { - Started, - Stopped, -}; +using namespace AudioCore::AudioOut; class IAudioOut final : public ServiceFramework<IAudioOut> { public: - explicit IAudioOut(Core::System& system_, AudoutParams audio_params_, - AudioCore::AudioOut& audio_core_, std::string&& device_name_, - std::string&& unique_name) + explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, + size_t session_id, std::string& device_name, + const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, - audio_core{audio_core_}, device_name{std::move(device_name_)}, - audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_, - "IAudioOut"} { + service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( + "AudioOutEvent")}, + impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { + // clang-format off static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, - {1, &IAudioOut::StartAudioOut, "Start"}, - {2, &IAudioOut::StopAudioOut, "Stop"}, - {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, + {1, &IAudioOut::Start, "Start"}, + {2, &IAudioOut::Stop, "Stop"}, + {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"}, {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, - {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"}, + {5, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffers"}, {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"}, - {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, - {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, + {7, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBufferAuto"}, + {8, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffersAuto"}, {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, {10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"}, {11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"}, @@ -64,241 +51,263 @@ public: // clang-format on RegisterHandlers(functions); - // This is the event handle used to check if the audio buffer was released - buffer_event = service_context.CreateEvent("IAudioOutBufferReleased"); - - stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, - audio_params.channel_count, std::move(unique_name), [this] { - const auto guard = LockService(); - buffer_event->GetWritableEvent().Signal(); - }); + if (impl->GetSystem() + .Initialize(device_name, in_params, handle, applet_resource_user_id) + .IsError()) { + LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!"); + } } ~IAudioOut() override { - service_context.CloseEvent(buffer_event); + impl->Free(); + service_context.CloseEvent(event); } -private: - struct AudioBuffer { - u64_le next; - u64_le buffer; - u64_le buffer_capacity; - u64_le buffer_size; - u64_le offset; - }; - static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size"); + [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() { + return impl; + } +private: void GetAudioOutState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto state = static_cast<u32>(impl->GetState()); + + LOG_DEBUG(Service_Audio, "called. State={}", state); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped)); + rb.Push(state); } - void StartAudioOut(Kernel::HLERequestContext& ctx) { + void Start(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - if (stream->IsPlaying()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_OPERATION_FAILED); - return; - } - - audio_core.StartStream(stream); + auto result = impl->StartSystem(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } - void StopAudioOut(Kernel::HLERequestContext& ctx) { + void Stop(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - if (stream->IsPlaying()) { - audio_core.StopStream(stream); - } + auto result = impl->StopSystem(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } - void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); - } - - void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description()); + void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + u64 tag = rp.PopRaw<u64>(); - const auto& input_buffer{ctx.ReadBuffer()}; - ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer), - "AudioBuffer input is an invalid size!"); - AudioBuffer audio_buffer{}; - std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer)); - const u64 tag{rp.Pop<u64>()}; + const auto in_buffer_size{ctx.GetReadBufferSize()}; + if (in_buffer_size < sizeof(AudioOutBuffer)) { + LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!"); + } - std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); - main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); + const auto& in_buffer = ctx.ReadBuffer(); + AudioOutBuffer buffer{}; + std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer)); - if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_BUFFER_COUNT_EXCEEDED); - return; - } + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag); + + auto result = impl->AppendBuffer(buffer, tag); IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + auto& buffer_event = impl->GetBufferEvent(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); + rb.PushCopyObjects(buffer_event); } - void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); + void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) { + auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64); + std::vector<u64> released_buffers(write_buffer_size, 0); - const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; - const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; + auto count = impl->GetReleasedBuffers(released_buffers); - std::vector<u64> tags{released_buffers}; - tags.resize(max_count); - ctx.WriteBuffer(tags); + [[maybe_unused]] std::string tags{}; + for (u32 i = 0; i < count; i++) { + tags += fmt::format("{:08X}, ", released_buffers[i]); + } + [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()}; + LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count, + tags); + ctx.WriteBuffer(released_buffers); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(released_buffers.size())); + rb.Push(count); } void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - IPC::RequestParser rp{ctx}; + const u64 tag{rp.Pop<u64>()}; + const auto buffer_queued{impl->ContainsAudioBuffer(tag)}; + + LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(stream->ContainsBuffer(tag)); + rb.Push(buffer_queued); } void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto buffer_count = impl->GetBufferCount(); + + LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count); IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(stream->GetQueueSize())); + rb.Push(buffer_count); } void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto samples_played = impl->GetPlayedSampleCount(); + + LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played); IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); - rb.Push(stream->GetPlayedSampleCount()); + rb.Push(samples_played); } void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + bool flushed{impl->FlushAudioOutBuffers()}; + + LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(stream->Flush()); + rb.Push(flushed); } void SetAudioOutVolume(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const float volume = rp.Pop<float>(); - LOG_DEBUG(Service_Audio, "called, volume={}", volume); + const auto volume = rp.Pop<f32>(); + + LOG_DEBUG(Service_Audio, "called. Volume={}", volume); - stream->SetVolume(volume); + impl->SetVolume(volume); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void GetAudioOutVolume(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto volume = impl->GetVolume(); + + LOG_DEBUG(Service_Audio, "called. Volume={}", volume); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(stream->GetVolume()); + rb.Push(volume); } - AudioCore::AudioOut& audio_core; - AudioCore::StreamPtr stream; - std::string device_name; - - [[maybe_unused]] AudoutParams audio_params{}; - - Core::Memory::Memory& main_memory; - KernelHelpers::ServiceContext service_context; - - /// This is the event handle used to check if the audio buffer was released - Kernel::KEvent* buffer_event; + Kernel::KEvent* event; + std::shared_ptr<AudioCore::AudioOut::Out> impl; }; -AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} { +AudOutU::AudOutU(Core::System& system_) + : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew}, + service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>( + system_)} { // clang-format off static const FunctionInfo functions[] = { - {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"}, - {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"}, - {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, - {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}, + {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, + {1, &AudOutU::OpenAudioOut, "OpenAudioOut"}, + {2, &AudOutU::ListAudioOuts, "ListAudioOutsAuto"}, + {3, &AudOutU::OpenAudioOut, "OpenAudioOutAuto"}, }; // clang-format on RegisterHandlers(functions); - audio_core = std::make_unique<AudioCore::AudioOut>(); } AudOutU::~AudOutU() = default; -void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); +void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { + using namespace AudioCore::AudioRenderer; - ctx.WriteBuffer(DefaultDevice); + std::scoped_lock l{impl->mutex}; + + const auto write_count = + static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); + std::vector<AudioDevice::AudioDeviceName> device_names{}; + std::string print_names{}; + if (write_count > 0) { + device_names.push_back(AudioDevice::AudioDeviceName("DeviceOut")); + LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut"); + } else { + LOG_DEBUG(Service_Audio, "called. Empty buffer passed in."); + } + + ctx.WriteBuffer(device_names); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(1); // Amount of audio devices + rb.Push<u32>(static_cast<u32>(device_names.size())); } -void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - +void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto in_params{rp.PopRaw<AudioOutParameter>()}; + auto applet_resource_user_id{rp.PopRaw<u64>()}; const auto device_name_data{ctx.ReadBuffer()}; - std::string device_name; - if (device_name_data[0] != '\0') { - device_name.assign(device_name_data.begin(), device_name_data.end()); - } else { - device_name.assign(DefaultDevice.begin(), DefaultDevice.end()); - } - ctx.WriteBuffer(device_name); + auto device_name = Common::StringFromBuffer(device_name_data); + auto handle{ctx.GetCopyHandle(0)}; - IPC::RequestParser rp{ctx}; - auto params{rp.PopRaw<AudoutParams>()}; - if (params.channel_count <= 2) { - // Mono does not exist for audout - params.channel_count = 2; - } else { - params.channel_count = 6; + auto link{impl->LinkToManager()}; + if (link.IsError()) { + LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(link); + return; } - if (!params.sample_rate) { - params.sample_rate = DefaultSampleRate; + + size_t new_session_id{}; + auto result{impl->AcquireSessionId(new_session_id)}; + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; } - std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())}; - auto audio_out_interface = std::make_shared<IAudioOut>( - system, params, *audio_core, std::move(device_name), std::move(unique_name)); + LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, + impl->num_free_sessions); + + auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, + in_params, handle, applet_resource_user_id); + + impl->sessions[new_session_id] = audio_out->GetImpl(); + impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; + + auto& out_system = impl->sessions[new_session_id]->GetSystem(); + AudioOutParameterInternal out_params{.sample_rate = out_system.GetSampleRate(), + .channel_count = out_system.GetChannelCount(), + .sample_format = + static_cast<u32>(out_system.GetSampleFormat()), + .state = static_cast<u32>(out_system.GetState())}; IPC::ResponseBuilder rb{ctx, 6, 0, 1}; - rb.Push(ResultSuccess); - rb.Push<u32>(DefaultSampleRate); - rb.Push<u32>(params.channel_count); - rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); - rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); - rb.PushIpcInterface<IAudioOut>(audio_out_interface); - audio_out_interfaces.push_back(std::move(audio_out_interface)); + ctx.WriteBuffer(out_system.GetName()); + + rb.Push(ResultSuccess); + rb.PushRaw<AudioOutParameterInternal>(out_params); + rb.PushIpcInterface<IAudioOut>(audio_out); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index d82004c2e..fdc0ee754 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -3,13 +3,11 @@ #pragma once -#include <vector> +#include "audio_core/audio_out_manager.h" +#include "audio_core/out/audio_out.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" -namespace AudioCore { -class AudioOut; -} - namespace Core { class System; } @@ -18,6 +16,11 @@ namespace Kernel { class HLERequestContext; } +namespace AudioCore::AudioOut { +class Manager; +class Out; +} // namespace AudioCore::AudioOut + namespace Service::Audio { class IAudioOut; @@ -28,11 +31,11 @@ public: ~AudOutU() override; private: - void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); - void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); + void ListAudioOuts(Kernel::HLERequestContext& ctx); + void OpenAudioOut(Kernel::HLERequestContext& ctx); - std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces; - std::unique_ptr<AudioCore::AudioOut> audio_core; + KernelHelpers::ServiceContext service_context; + std::unique_ptr<AudioCore::AudioOut::Manager> impl; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 2ce63c004..381a66ba5 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -4,7 +4,12 @@ #include <array> #include <memory> -#include "audio_core/audio_renderer.h" +#include "audio_core/audio_core.h" +#include "audio_core/common/audio_renderer_parameter.h" +#include "audio_core/common/feature_support.h" +#include "audio_core/renderer/audio_device.h" +#include "audio_core/renderer/audio_renderer.h" +#include "audio_core/renderer/voice/voice_info.h" #include "common/alignment.h" #include "common/bit_util.h" #include "common/common_funcs.h" @@ -13,91 +18,112 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/service/audio/audren_u.h" #include "core/hle/service/audio/errors.h" +#include "core/memory.h" + +using namespace AudioCore::AudioRenderer; namespace Service::Audio { class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { public: - explicit IAudioRenderer(Core::System& system_, - const AudioCommon::AudioRendererParameter& audren_params, - const std::size_t instance_number) + explicit IAudioRenderer(Core::System& system_, Manager& manager_, + AudioCore::AudioRendererParameterInternal& params, + Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, + u32 process_handle, u64 applet_resource_user_id, s32 session_id) : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew}, - service_context{system_, "IAudioRenderer"} { + service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent( + "IAudioRendererEvent")}, + manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, {3, &IAudioRenderer::GetState, "GetState"}, - {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"}, + {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, {5, &IAudioRenderer::Start, "Start"}, {6, &IAudioRenderer::Stop, "Stop"}, {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, - {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"}, - {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"}, + {10, nullptr, "RequestUpdateAuto"}, + {11, nullptr, "ExecuteAudioRendererRendering"}, }; // clang-format on RegisterHandlers(functions); - system_event = service_context.CreateEvent("IAudioRenderer:SystemEvent"); - renderer = std::make_unique<AudioCore::AudioRenderer>( - system.CoreTiming(), system.Memory(), audren_params, - [this]() { - const auto guard = LockService(); - system_event->GetWritableEvent().Signal(); - }, - instance_number); + impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle, + applet_resource_user_id, session_id); } ~IAudioRenderer() override { - service_context.CloseEvent(system_event); + impl->Finalize(); + service_context.CloseEvent(rendered_event); } private: void GetSampleRate(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto sample_rate{impl->GetSystem().GetSampleRate()}; + + LOG_DEBUG(Service_Audio, "called. Sample rate {}", sample_rate); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(renderer->GetSampleRate()); + rb.Push(sample_rate); } void GetSampleCount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const auto sample_count{impl->GetSystem().GetSampleCount()}; + + LOG_DEBUG(Service_Audio, "called. Sample count {}", sample_count); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(renderer->GetSampleCount()); + rb.Push(sample_count); } void GetState(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const u32 state{!impl->GetSystem().IsActive()}; + + LOG_DEBUG(Service_Audio, "called, state {}", state); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); + rb.Push(state); } void GetMixBufferCount(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); + const auto buffer_count{impl->GetSystem().GetMixBufferCount()}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push<u32>(renderer->GetMixBufferCount()); + rb.Push(buffer_count); } - void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "(STUBBED) called"); + void RequestUpdate(Kernel::HLERequestContext& ctx) { + LOG_TRACE(Service_Audio, "called"); - std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0); - auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); + std::vector<u8> input{ctx.ReadBuffer(0)}; + + // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for + // checking size 0. Performance size is 0 for most games. + const auto buffers{ctx.BufferDescriptorB()}; + std::vector<u8> output(buffers[0].Size(), 0); + std::vector<u8> performance(buffers[1].Size(), 0); + + auto result = impl->RequestUpdate(input, performance, output); if (result.IsSuccess()) { - ctx.WriteBuffer(output_params); + ctx.WriteBufferB(output.data(), output.size(), 0); + ctx.WriteBufferB(performance.data(), performance.size(), 1); + } else { + LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); } IPC::ResponseBuilder rb{ctx, 2}; @@ -105,38 +131,45 @@ private: } void Start(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); - const auto result = renderer->Start(); + impl->Start(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void Stop(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); - const auto result = renderer->Stop(); + impl->Stop(); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + rb.Push(ResultSuccess); } void QuerySystemEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); + + if (impl->GetSystem().GetExecutionMode() == AudioCore::ExecutionMode::Manual) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_SUPPORTED); + return; + } IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(system_event->GetReadableEvent()); + rb.PushCopyObjects(rendered_event->GetReadableEvent()); } void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::RequestParser rp{ctx}; - rendering_time_limit_percent = rp.Pop<u32>(); - LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}", - rendering_time_limit_percent); + auto limit = rp.PopRaw<u32>(); - ASSERT(rendering_time_limit_percent <= 100); + auto& system_ = impl->GetSystem(); + system_.SetRenderingTimeLimit(limit); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -145,34 +178,34 @@ private: void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); + auto& system_ = impl->GetSystem(); + auto time = system_.GetRenderingTimeLimit(); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(rendering_time_limit_percent); + rb.Push(time); } void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - - // This service command currently only reports an unsupported operation - // error code, or aborts. Given that, we just always return an error - // code in this case. - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_NOT_SUPPORTED); } KernelHelpers::ServiceContext service_context; - - Kernel::KEvent* system_event; - std::unique_ptr<AudioCore::AudioRenderer> renderer; - u32 rendering_time_limit_percent = 100; + Kernel::KEvent* rendered_event; + Manager& manager; + std::unique_ptr<Renderer> impl; }; class IAudioDevice final : public ServiceFramework<IAudioDevice> { + public: - explicit IAudioDevice(Core::System& system_, Kernel::KEvent* buffer_event_, u32_le revision_) - : ServiceFramework{system_, "IAudioDevice"}, buffer_event{buffer_event_}, revision{ - revision_} { + explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, + u32 device_num) + : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew}, + service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>( + system_, applet_resource_user_id, + revision)}, + event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { static const FunctionInfo functions[] = { {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"}, @@ -186,54 +219,45 @@ public: {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, {11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"}, {12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"}, - {13, nullptr, "GetActiveAudioOutputDeviceName"}, - {14, nullptr, "ListAudioOutputDeviceName"}, + {13, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioOutputDeviceName"}, + {14, &IAudioDevice::ListAudioOutputDeviceName, "ListAudioOutputDeviceName"}, }; RegisterHandlers(functions); + + event->GetWritableEvent().Signal(); } -private: - using AudioDeviceName = std::array<char, 256>; - static constexpr std::array<std::string_view, 4> audio_device_names{{ - "AudioStereoJackOutput", - "AudioBuiltInSpeakerOutput", - "AudioTvOutput", - "AudioUsbDeviceOutput", - }}; - enum class DeviceType { - AHUBHeadphones, - AHUBSpeakers, - HDA, - USBOutput, - }; + ~IAudioDevice() override { + service_context.CloseEvent(event); + } +private: void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); - const bool usb_output_supported = - IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision); - const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName); + std::vector<AudioDevice::AudioDeviceName> out_names{}; - std::vector<AudioDeviceName> name_buffer; - name_buffer.reserve(audio_device_names.size()); + u32 out_count = impl->ListAudioDeviceName(out_names, in_count); - for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) { - const auto type = static_cast<DeviceType>(i); - - if (!usb_output_supported && type == DeviceType::USBOutput) { - continue; + std::string out{}; + for (u32 i = 0; i < out_count; i++) { + std::string a{}; + u32 j = 0; + while (out_names[i].name[j] != '\0') { + a += out_names[i].name[j]; + j++; } - - const auto& device_name = audio_device_names[i]; - auto& entry = name_buffer.emplace_back(); - device_name.copy(entry.data(), device_name.size()); + out += "\n\t" + a; } - ctx.WriteBuffer(name_buffer); + LOG_DEBUG(Service_Audio, "called.\nNames={}", out); IPC::ResponseBuilder rb{ctx, 3}; + + ctx.WriteBuffer(out_names); + rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(name_buffer.size())); + rb.Push(out_count); } void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { @@ -243,7 +267,11 @@ private: const auto device_name_buffer = ctx.ReadBuffer(); const std::string name = Common::StringFromBuffer(device_name_buffer); - LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume); + LOG_DEBUG(Service_Audio, "called. name={}, volume={}", name, volume); + + if (name == "AudioTvOutput") { + impl->SetDeviceVolumes(volume); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -253,53 +281,60 @@ private: const auto device_name_buffer = ctx.ReadBuffer(); const std::string name = Common::StringFromBuffer(device_name_buffer); - LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name); + LOG_DEBUG(Service_Audio, "called. Name={}", name); + + f32 volume{1.0f}; + if (name == "AudioTvOutput") { + volume = impl->GetDeviceVolume(name); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(1.0f); + rb.Push(volume); } void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + const auto write_size = ctx.GetWriteBufferSize() / sizeof(char); + std::string out_name{"AudioTvOutput"}; - // Currently set to always be TV audio output. - const auto& device_name = audio_device_names[2]; + LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name); - AudioDeviceName out_device_name{}; - device_name.copy(out_device_name.data(), device_name.size()); + out_name.resize(write_size); - ctx.WriteBuffer(out_device_name); + ctx.WriteBuffer(out_name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "(STUBBED) called"); - buffer_event->GetWritableEvent().Signal(); + event->GetWritableEvent().Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); + rb.PushCopyObjects(event->GetReadableEvent()); } void GetActiveChannelCount(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + const auto& sink{system.AudioCore().GetOutputSink()}; + u32 channel_count{sink.GetDeviceChannels()}; + + LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count); IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); - rb.Push<u32>(2); + rb.Push<u32>(channel_count); } - // Should be similar to QueryAudioDeviceOutputEvent void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); + rb.PushCopyObjects(event->GetReadableEvent()); } void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) { @@ -307,402 +342,167 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(buffer_event->GetReadableEvent()); + rb.PushCopyObjects(event->GetReadableEvent()); } - Kernel::KEvent* buffer_event; - u32_le revision = 0; + void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) { + const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName); + + std::vector<AudioDevice::AudioDeviceName> out_names{}; + + u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count); + + std::string out{}; + for (u32 i = 0; i < out_count; i++) { + std::string a{}; + u32 j = 0; + while (out_names[i].name[j] != '\0') { + a += out_names[i].name[j]; + j++; + } + out += "\n\t" + a; + } + + LOG_DEBUG(Service_Audio, "called.\nNames={}", out); + + IPC::ResponseBuilder rb{ctx, 3}; + + ctx.WriteBuffer(out_names); + + rb.Push(ResultSuccess); + rb.Push(out_count); + } + + KernelHelpers::ServiceContext service_context; + std::unique_ptr<AudioDevice> impl; + Kernel::KEvent* event; }; AudRenU::AudRenU(Core::System& system_) - : ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"} { + : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew}, + service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { // clang-format off static const FunctionInfo functions[] = { {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, - {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetWorkBufferSize"}, + {1, &AudRenU::GetWorkBufferSize, "GetWorkBufferSize"}, {2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"}, - {3, &AudRenU::OpenAudioRendererForManualExecution, "OpenAudioRendererForManualExecution"}, + {3, nullptr, "OpenAudioRendererForManualExecution"}, {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"}, }; // clang-format on RegisterHandlers(functions); - - buffer_event = service_context.CreateEvent("IAudioOutBufferReleasedEvent"); } -AudRenU::~AudRenU() { - service_context.CloseEvent(buffer_event); -} +AudRenU::~AudRenU() = default; void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - - OpenAudioRendererImpl(ctx); -} - -static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) { - // +1 represents the final mix. - return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count + - 1; -} - -void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); - - // Several calculations below align the sizes being calculated - // onto a 64 byte boundary. - static constexpr u64 buffer_alignment_size = 64; - - // Some calculations that calculate portions of the buffer - // that will contain information, on the other hand, align - // the result of some of their calcularions on a 16 byte boundary. - static constexpr u64 info_field_alignment_size = 16; - - // Maximum detail entries that may exist at one time for performance - // frame statistics. - static constexpr u64 max_perf_detail_entries = 100; - - // Size of the data structure representing the bulk of the voice-related state. - static constexpr u64 voice_state_size_bytes = 0x100; - - // Size of the upsampler manager data structure - constexpr u64 upsampler_manager_size = 0x48; - - // Calculates the part of the size that relates to mix buffers. - const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) { - // As of 8.0.0 this is the maximum on voice channels. - constexpr u64 max_voice_channels = 6; - - // The service expects the sample_count member of the parameters to either be - // a value of 160 or 240, so the maximum sample count is assumed in order - // to adequately handle all values at runtime. - constexpr u64 default_max_sample_count = 240; - - const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels; - - u64 size = 0; - size += total_mix_buffers * (sizeof(s32) * params.sample_count); - size += total_mix_buffers * (sizeof(s32) * default_max_sample_count); - size += u64{params.submix_count} + params.sink_count; - size = Common::AlignUp(size, buffer_alignment_size); - size += Common::AlignUp(params.unknown_30, buffer_alignment_size); - size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size); - return size; - }; - - // Calculates the portion of the size related to the mix data (and the sorting thereof). - const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) { - // The size of the mixing info data structure. - constexpr u64 mix_info_size = 0x940; - - // Consists of total submixes with the final mix included. - const u64 total_mix_count = u64{params.submix_count} + 1; - - // The total number of effects that may be available to the audio renderer at any time. - constexpr u64 max_effects = 256; - - // Calculates the part of the size related to the audio node state. - // This will only be used if the audio revision supports the splitter. - const auto calculate_node_state_size = [](std::size_t num_nodes) { - // Internally within a nodestate, it appears to use a data structure - // similar to a std::bitset<64> twice. - constexpr u64 bit_size = Common::BitSize<u64>(); - constexpr u64 num_bitsets = 2; - - // Node state instances have three states internally for performing - // depth-first searches of nodes. Initialized, Found, and Done Sorting. - constexpr u64 num_states = 3; - - u64 size = 0; - size += (num_nodes * num_nodes) * sizeof(s32); - size += num_states * (num_nodes * sizeof(s32)); - size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>()); - return size; - }; - - // Calculates the part of the size related to the adjacency (aka edge) matrix. - const auto calculate_edge_matrix_size = [](std::size_t num_nodes) { - return (num_nodes * num_nodes) * sizeof(s32); - }; - - u64 size = 0; - size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size); - size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size); - size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count, - info_field_alignment_size); - - if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { - size += Common::AlignUp(calculate_node_state_size(total_mix_count) + - calculate_edge_matrix_size(total_mix_count), - info_field_alignment_size); - } - - return size; - }; - - // Calculates the part of the size related to voice channel info. - const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 voice_info_size = 0x220; - constexpr u64 voice_resource_size = 0xD0; - - u64 size = 0; - size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size); - size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size); - size += - Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size); - size += - Common::AlignUp(voice_state_size_bytes * params.voice_count, info_field_alignment_size); - return size; - }; - - // Calculates the part of the size related to memory pools. - const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) { - const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count); - const u64 memory_pool_info_size = 0x20; - return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size); - }; - - // Calculates the part of the size related to the splitter context. - const auto calculate_splitter_context_size = - [](const AudioCommon::AudioRendererParameter& params) -> u64 { - if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { - return 0; - } - - constexpr u64 splitter_info_size = 0x20; - constexpr u64 splitter_destination_data_size = 0xE0; - - u64 size = 0; - size += params.num_splitter_send_channels; - size += - Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size); - size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels, - info_field_alignment_size); - - return size; - }; - - // Calculates the part of the size related to the upsampler info. - const auto calculate_upsampler_info_size = - [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 upsampler_info_size = 0x280; - // Yes, using the buffer size over info alignment size is intentional here. - return Common::AlignUp(upsampler_info_size * - (u64{params.submix_count} + params.sink_count), - buffer_alignment_size); - }; - - // Calculates the part of the size related to effect info. - const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 effect_info_size = 0x2B0; - return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size); - }; - - // Calculates the part of the size related to audio sink info. - const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) { - const u64 sink_info_size = 0x170; - return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size); - }; - - // Calculates the part of the size related to voice state info. - const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) { - const u64 voice_state_size = 0x100; - const u64 additional_size = buffer_alignment_size - 1; - return Common::AlignUp(voice_state_size * params.voice_count + additional_size, - info_field_alignment_size); - }; - - // Calculates the part of the size related to performance statistics. - const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) { - // Extra size value appended to the end of the calculation. - constexpr u64 appended = 128; - - // Whether or not we assume the newer version of performance metrics data structures. - const bool is_v2 = - IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision); - - // Data structure sizes - constexpr u64 perf_statistics_size = 0x0C; - const u64 header_size = is_v2 ? 0x30 : 0x18; - const u64 entry_size = is_v2 ? 0x18 : 0x10; - const u64 detail_size = is_v2 ? 0x18 : 0x10; - - const u64 entry_count = CalculateNumPerformanceEntries(params); - const u64 size_per_frame = - header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries); - - u64 size = 0; - size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1, - buffer_alignment_size); - size += Common::AlignUp(perf_statistics_size, buffer_alignment_size); - size += appended; - return size; - }; - - // Calculates the part of the size that relates to the audio command buffer. - const auto calculate_command_buffer_size = - [](const AudioCommon::AudioRendererParameter& params) { - constexpr u64 alignment = (buffer_alignment_size - 1) * 2; - - if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { - constexpr u64 command_buffer_size = 0x18000; - - return command_buffer_size + alignment; - } - - // When the variadic command buffer is supported, this means - // the command generator for the audio renderer can issue commands - // that are (as one would expect), variable in size. So what we need to do - // is determine the maximum possible size for a few command data structures - // then multiply them by the amount of present commands indicated by the given - // respective audio parameters. - - constexpr u64 max_biquad_filters = 2; - constexpr u64 max_mix_buffers = 24; - - constexpr u64 biquad_filter_command_size = 0x2C; - - constexpr u64 depop_mix_command_size = 0x24; - constexpr u64 depop_setup_command_size = 0x50; - - constexpr u64 effect_command_max_size = 0x540; - - constexpr u64 mix_command_size = 0x1C; - constexpr u64 mix_ramp_command_size = 0x24; - constexpr u64 mix_ramp_grouped_command_size = 0x13C; - - constexpr u64 perf_command_size = 0x28; - - constexpr u64 sink_command_size = 0x130; - - constexpr u64 submix_command_max_size = - depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; - - constexpr u64 volume_command_size = 0x1C; - constexpr u64 volume_ramp_command_size = 0x20; - - constexpr u64 voice_biquad_filter_command_size = - biquad_filter_command_size * max_biquad_filters; - constexpr u64 voice_data_command_size = 0x9C; - const u64 voice_command_max_size = - (params.splitter_count * depop_setup_command_size) + - (voice_data_command_size + voice_biquad_filter_command_size + - volume_ramp_command_size + mix_ramp_grouped_command_size); + IPC::RequestParser rp{ctx}; - // Now calculate the individual elements that comprise the size and add them together. - const u64 effect_commands_size = params.effect_count * effect_command_max_size; + AudioCore::AudioRendererParameterInternal params; + rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params); + auto transfer_memory_handle = ctx.GetCopyHandle(0); + auto process_handle = ctx.GetCopyHandle(1); + auto transfer_memory_size = rp.Pop<u64>(); + auto applet_resource_user_id = rp.Pop<u64>(); - const u64 final_mix_commands_size = - depop_mix_command_size + volume_command_size * max_mix_buffers; + if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) { + LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_MAXIMUM_SESSIONS_REACHED); + return; + } - const u64 perf_commands_size = - perf_command_size * - (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); + const auto& handle_table{system.CurrentProcess()->GetHandleTable()}; + auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)}; + auto transfer_memory{ + process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)}; - const u64 sink_commands_size = params.sink_count * sink_command_size; + const auto session_id{impl->GetSessionId()}; + if (session_id == -1) { + LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_MAXIMUM_SESSIONS_REACHED); + return; + } - const u64 splitter_commands_size = - params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; + LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id, + impl->GetSessionCount()); - const u64 submix_commands_size = params.submix_count * submix_command_max_size; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(), + transfer_memory_size, process_handle, + applet_resource_user_id, session_id); +} - const u64 voice_commands_size = params.voice_count * voice_command_max_size; - - return effect_commands_size + final_mix_commands_size + perf_commands_size + - sink_commands_size + splitter_commands_size + submix_commands_size + - voice_commands_size + alignment; - }; +void AudRenU::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { + AudioCore::AudioRendererParameterInternal params; IPC::RequestParser rp{ctx}; - const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); - - u64 size = 0; - size += calculate_mix_buffer_sizes(params); - size += calculate_mix_info_size(params); - size += calculate_voice_info_size(params); - size += upsampler_manager_size; - size += calculate_memory_pools_size(params); - size += calculate_splitter_context_size(params); - - size = Common::AlignUp(size, buffer_alignment_size); - - size += calculate_upsampler_info_size(params); - size += calculate_effect_info_size(params); - size += calculate_sink_info_size(params); - size += calculate_voice_state_size(params); - size += calculate_perf_size(params); - size += calculate_command_buffer_size(params); - - // finally, 4KB page align the size, and we're done. - size = Common::AlignUp(size, 4096); + rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params); + + u64 size{0}; + auto result = impl->GetWorkBufferSize(params, size); + + std::string output_info{}; + output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision)); + output_info += + fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count); + output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}", + static_cast<u32>(params.execution_mode), params.voice_drop_enabled); + output_info += fmt::format( + "\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos " + "{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External " + "Context {:04X}", + params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos, + params.splitter_destinations, params.voices, params.perf_frames, + params.external_context_size); + + LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}", + output_info, size); IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); + rb.Push(result); rb.Push<u64>(size); - - LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size); } void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const u64 aruid = rp.Pop<u64>(); - LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid); + const auto applet_resource_user_id = rp.Pop<u64>(); + + LOG_DEBUG(Service_Audio, "called. Applet resource id {}", applet_resource_user_id); - // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that - // always assumes the initial release revision (REV1). IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioDevice>(system, buffer_event, Common::MakeMagic('R', 'E', 'V', '1')); + rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, + ::Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++); } void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - - OpenAudioRendererImpl(ctx); } void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { struct Parameters { u32 revision; - u64 aruid; + u64 applet_resource_user_id; }; IPC::RequestParser rp{ctx}; - const auto [revision, aruid] = rp.PopRaw<Parameters>(); - LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid); + const auto [revision, applet_resource_user_id] = rp.PopRaw<Parameters>(); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioDevice>(system, buffer_event, revision); -} + LOG_DEBUG(Service_Audio, "called. Revision {} Applet resource id {}", + AudioCore::GetRevisionNum(revision), applet_resource_user_id); -void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++); -} - -bool IsFeatureSupported(AudioFeatures feature, u32_le revision) { - // Byte swap - const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0'); - - switch (feature) { - case AudioFeatures::AudioUSBDeviceOutput: - return version_num >= 4U; - case AudioFeatures::Splitter: - return version_num >= 2U; - case AudioFeatures::PerformanceMetricsVersion2: - case AudioFeatures::VariadicCommandBuffer: - return version_num >= 5U; - default: - return false; - } + rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, revision, + num_audio_devices++); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 869d39002..4384a9b3c 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -3,6 +3,7 @@ #pragma once +#include "audio_core/audio_render_manager.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" @@ -15,6 +16,7 @@ class HLERequestContext; } namespace Service::Audio { +class IAudioRenderer; class AudRenU final : public ServiceFramework<AudRenU> { public: @@ -23,28 +25,14 @@ public: private: void OpenAudioRenderer(Kernel::HLERequestContext& ctx); - void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); + void GetWorkBufferSize(Kernel::HLERequestContext& ctx); void GetAudioDeviceService(Kernel::HLERequestContext& ctx); void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx); void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx); - void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx); - KernelHelpers::ServiceContext service_context; - - std::size_t audren_instance_count = 0; - Kernel::KEvent* buffer_event; + std::unique_ptr<AudioCore::AudioRenderer::Manager> impl; + u32 num_audio_devices{0}; }; -// Describes a particular audio feature that may be supported in a particular revision. -enum class AudioFeatures : u32 { - AudioUSBDeviceOutput, - Splitter, - PerformanceMetricsVersion2, - VariadicCommandBuffer, -}; - -// Tests if a particular audio feature is supported with a given audio revision. -bool IsFeatureSupported(AudioFeatures feature, u32_le revision); - } // namespace Service::Audio diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h index ac6c514af..d706978cb 100644 --- a/src/core/hle/service/audio/errors.h +++ b/src/core/hle/service/audio/errors.h @@ -7,8 +7,17 @@ namespace Service::Audio { +constexpr Result ERR_INVALID_DEVICE_NAME{ErrorModule::Audio, 1}; constexpr Result ERR_OPERATION_FAILED{ErrorModule::Audio, 2}; +constexpr Result ERR_INVALID_SAMPLE_RATE{ErrorModule::Audio, 3}; +constexpr Result ERR_INSUFFICIENT_BUFFER_SIZE{ErrorModule::Audio, 4}; +constexpr Result ERR_MAXIMUM_SESSIONS_REACHED{ErrorModule::Audio, 5}; constexpr Result ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8}; +constexpr Result ERR_INVALID_CHANNEL_COUNT{ErrorModule::Audio, 10}; +constexpr Result ERR_INVALID_UPDATE_DATA{ErrorModule::Audio, 41}; +constexpr Result ERR_POOL_MAPPING_FAILED{ErrorModule::Audio, 42}; constexpr Result ERR_NOT_SUPPORTED{ErrorModule::Audio, 513}; +constexpr Result ERR_INVALID_PROCESS_HANDLE{ErrorModule::Audio, 1536}; +constexpr Result ERR_INVALID_REVISION{ErrorModule::Audio, 1537}; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 75da659e5..4f2ed2d52 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -298,7 +298,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) { const auto sample_rate = rp.Pop<u32>(); const auto channel_count = rp.Pop<u32>(); - LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); + LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || sample_rate == 12000 || sample_rate == 8000, diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp index d928e37fb..bc08ac487 100644 --- a/src/core/hle/service/bcat/bcat_module.cpp +++ b/src/core/hle/service/bcat/bcat_module.cpp @@ -140,8 +140,8 @@ public: {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, - {30101, nullptr, "Unknown"}, - {30102, nullptr, "Unknown2"}, + {30101, nullptr, "Unknown30101"}, + {30102, nullptr, "Unknown30102"}, {30200, nullptr, "RegisterBackgroundDeliveryTask"}, {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, {30202, nullptr, "BlockDeliveryTask"}, diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index f9ee2b624..ec7e5320c 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -181,6 +181,11 @@ public: {147, nullptr, "RegisterAudioControlNotification"}, {148, nullptr, "SendAudioControlPassthroughCommand"}, {149, nullptr, "SendAudioControlSetAbsoluteVolumeCommand"}, + {150, nullptr, "AcquireAudioSinkVolumeLocallyChangedEvent"}, + {151, nullptr, "AcquireAudioSinkVolumeUpdateRequestCompletedEvent"}, + {152, nullptr, "GetAudioSinkVolume"}, + {153, nullptr, "RequestUpdateAudioSinkVolume"}, + {154, nullptr, "IsAudioSinkVolumeSupported"}, {256, nullptr, "IsManufacturingMode"}, {257, nullptr, "EmulateBluetoothCrash"}, {258, nullptr, "GetBleChannelMap"}, diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 3fa88cbd3..eebf85e03 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -214,8 +214,12 @@ public: {76, nullptr, "Unknown76"}, {100, nullptr, "Unknown100"}, {101, nullptr, "Unknown101"}, - {110, nullptr, "Unknown102"}, - {111, nullptr, "Unknown103"}, + {110, nullptr, "Unknown110"}, + {111, nullptr, "Unknown111"}, + {112, nullptr, "Unknown112"}, + {113, nullptr, "Unknown113"}, + {114, nullptr, "Unknown114"}, + {115, nullptr, "Unknown115"}, }; // clang-format on diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp index 7d35b4208..4a81bb5e2 100644 --- a/src/core/hle/service/fatal/fatal_p.cpp +++ b/src/core/hle/service/fatal/fatal_p.cpp @@ -6,7 +6,13 @@ namespace Service::Fatal { Fatal_P::Fatal_P(std::shared_ptr<Module> module_, Core::System& system_) - : Interface(std::move(module_), system_, "fatal:p") {} + : Interface(std::move(module_), system_, "fatal:p") { + static const FunctionInfo functions[] = { + {0, nullptr, "GetFatalEvent"}, + {10, nullptr, "GetFatalContext"}, + }; + RegisterHandlers(functions); +} Fatal_P::~Fatal_P() = default; diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h index 46282f42e..4613a4e60 100644 --- a/src/core/hle/service/hid/errors.h +++ b/src/core/hle/service/hid/errors.h @@ -19,3 +19,10 @@ constexpr Result InvalidNpadId{ErrorModule::HID, 709}; constexpr Result NpadNotConnected{ErrorModule::HID, 710}; } // namespace Service::HID + +namespace Service::IRS { + +constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78}; +constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 89bb12442..5ecbddf94 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -2345,8 +2345,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system std::make_shared<HidSys>(system)->InstallAsService(service_manager); std::make_shared<HidTmp>(system)->InstallAsService(service_manager); - std::make_shared<IRS>(system)->InstallAsService(service_manager); - std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager); + std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager); + std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager); std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager); } diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index d2a91d913..d5107e41f 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -1,16 +1,28 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <algorithm> +#include <random> + #include "core/core.h" #include "core/core_timing.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/irs.h" +#include "core/hle/service/hid/irsensor/clustering_processor.h" +#include "core/hle/service/hid/irsensor/image_transfer_processor.h" +#include "core/hle/service/hid/irsensor/ir_led_processor.h" +#include "core/hle/service/hid/irsensor/moment_processor.h" +#include "core/hle/service/hid/irsensor/pointing_processor.h" +#include "core/hle/service/hid/irsensor/tera_plugin_processor.h" +#include "core/memory.h" -namespace Service::HID { +namespace Service::IRS { IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { // clang-format off @@ -36,14 +48,19 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { }; // clang-format on + u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer(); RegisterHandlers(functions); + shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory)); + + npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); } +IRS::~IRS() = default; void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -54,7 +71,7 @@ void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -75,7 +92,7 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - IrCameraHandle camera_handle; + Core::IrSensor::IrCameraHandle camera_handle; INSERT_PADDING_WORDS_NOINIT(1); u64 applet_resource_user_id; }; @@ -88,17 +105,23 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, parameters.applet_resource_user_id); + auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsSuccess()) { + // TODO: Stop Image processor + result = ResultSuccess; + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - IrCameraHandle camera_handle; + Core::IrSensor::IrCameraHandle camera_handle; INSERT_PADDING_WORDS_NOINIT(1); u64 applet_resource_user_id; - PackedMomentProcessorConfig processor_config; + Core::IrSensor::PackedMomentProcessorConfig processor_config; }; static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); @@ -109,19 +132,28 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, parameters.applet_resource_user_id); + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessor<MomentProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - IrCameraHandle camera_handle; + Core::IrSensor::IrCameraHandle camera_handle; INSERT_PADDING_WORDS_NOINIT(1); u64 applet_resource_user_id; - PackedClusteringProcessorConfig processor_config; + Core::IrSensor::PackedClusteringProcessorConfig processor_config; }; - static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size."); + static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); const auto parameters{rp.PopRaw<Parameters>()}; @@ -130,17 +162,27 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, parameters.applet_resource_user_id); + auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessor<ClusteringProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<ClusteringProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - IrCameraHandle camera_handle; + Core::IrSensor::IrCameraHandle camera_handle; INSERT_PADDING_WORDS_NOINIT(1); u64 applet_resource_user_id; - PackedImageTransferProcessorConfig processor_config; + Core::IrSensor::PackedImageTransferProcessorConfig processor_config; u32 transfer_memory_size; }; static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); @@ -151,20 +193,42 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { auto t_mem = system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); - LOG_WARNING(Service_IRS, - "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, " - "applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.transfer_memory_size, parameters.applet_resource_user_id); + if (t_mem.IsNull()) { + LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size"); + + u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress()); + + LOG_INFO(Service_IRS, + "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " + "applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id); + + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<ImageTransferProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + image_transfer_processor.SetTransferMemoryPointer(transfer_memory); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - IrCameraHandle camera_handle; + Core::IrSensor::IrCameraHandle camera_handle; INSERT_PADDING_WORDS_NOINIT(1); u64 applet_resource_user_id; }; @@ -172,32 +236,68 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw<Parameters>()}; - LOG_WARNING(Service_IRS, - "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.applet_resource_user_id); + LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); + + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + + if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidProcessorState); + return; + } - IPC::ResponseBuilder rb{ctx, 5}; + std::vector<u8> data{}; + const auto& image_transfer_processor = + GetProcessor<ImageTransferProcessor>(parameters.camera_handle); + const auto& state = image_transfer_processor.GetState(data); + + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 6}; rb.Push(ResultSuccess); - rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks()); - rb.PushRaw<u32>(0); + rb.PushRaw(state); } void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; - const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + Core::IrSensor::IrCameraHandle camera_handle; + Core::IrSensor::PackedTeraPluginProcessorConfig processor_config; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - LOG_WARNING(Service_IRS, - "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " - "applet_resource_user_id={}", - camera_handle.npad_type, camera_handle.npad_id, processor_config.mode, - processor_config.required_mcu_version.major, - processor_config.required_mcu_version.minor, applet_resource_user_id); + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " + "applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major, + parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id); + + const auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<TeraPluginProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { @@ -207,17 +307,17 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && npad_id != Core::HID::NpadIdType::Handheld) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(InvalidNpadId); + rb.Push(Service::HID::InvalidNpadId); return; } - IrCameraHandle camera_handle{ + Core::IrSensor::IrCameraHandle camera_handle{ .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)), .npad_type = Core::HID::NpadStyleIndex::None, }; - LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}", - npad_id, camera_handle.npad_id, camera_handle.npad_type); + LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, + camera_handle.npad_id, camera_handle.npad_type); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -226,8 +326,8 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; - const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; LOG_WARNING( @@ -236,14 +336,23 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, processor_config.required_mcu_version.minor, applet_resource_user_id); + auto result = IsIrCameraHandleValid(camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessor<PointingProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - IrCameraHandle camera_handle; + Core::IrSensor::IrCameraHandle camera_handle; INSERT_PADDING_WORDS_NOINIT(1); u64 applet_resource_user_id; }; @@ -256,14 +365,20 @@ void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, parameters.applet_resource_user_id); + auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsSuccess()) { + // TODO: Suspend image processor + result = ResultSuccess; + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; - const auto mcu_version{rp.PopRaw<PackedMcuVersion>()}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; LOG_WARNING( @@ -272,37 +387,45 @@ void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, mcu_version.minor); + auto result = IsIrCameraHandleValid(camera_handle); + if (result.IsSuccess()) { + // TODO: Check firmware version + result = ResultSuccess; + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - struct Parameters { - IrCameraHandle camera_handle; - PackedFunctionLevel function_level; - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_WARNING(Service_IRS, - "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.applet_resource_user_id); + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", + camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, + applet_resource_user_id); + + auto result = IsIrCameraHandleValid(camera_handle); + if (result.IsSuccess()) { + // TODO: Set Function level + result = ResultSuccess; + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - IrCameraHandle camera_handle; + Core::IrSensor::IrCameraHandle camera_handle; INSERT_PADDING_WORDS_NOINIT(1); u64 applet_resource_user_id; - PackedImageTransferProcessorExConfig processor_config; + Core::IrSensor::PackedImageTransferProcessorExConfig processor_config; u64 transfer_memory_size; }; static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); @@ -313,20 +436,33 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { auto t_mem = system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); - LOG_WARNING(Service_IRS, - "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, " - "applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.transfer_memory_size, parameters.applet_resource_user_id); + u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress()); + + LOG_INFO(Service_IRS, + "called, npad_type={}, npad_id={}, transfer_memory_size={}, " + "applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.transfer_memory_size, parameters.applet_resource_user_id); + + auto result = IsIrCameraHandleValid(parameters.camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); + auto& image_transfer_processor = + GetProcessor<ImageTransferProcessor>(parameters.camera_handle); + image_transfer_processor.SetConfig(parameters.processor_config); + image_transfer_processor.SetTransferMemoryPointer(transfer_memory); + } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; - const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()}; + const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; + const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; LOG_WARNING(Service_IRS, @@ -336,14 +472,23 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { processor_config.required_mcu_version.major, processor_config.required_mcu_version.minor, applet_resource_user_id); + auto result = IsIrCameraHandleValid(camera_handle); + + if (result.IsSuccess()) { + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessor<IrLedProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - IrCameraHandle camera_handle; + Core::IrSensor::IrCameraHandle camera_handle; INSERT_PADDING_WORDS_NOINIT(1); u64 applet_resource_user_id; }; @@ -356,14 +501,20 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, parameters.applet_resource_user_id); + auto result = IsIrCameraHandleValid(parameters.camera_handle); + if (result.IsSuccess()) { + // TODO: Stop image processor async + result = ResultSuccess; + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - PackedFunctionLevel function_level; + Core::IrSensor::PackedFunctionLevel function_level; INSERT_PADDING_WORDS_NOINIT(1); u64 applet_resource_user_id; }; @@ -378,7 +529,22 @@ void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } -IRS::~IRS() = default; +Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { + if (camera_handle.npad_id > + static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) { + return InvalidIrCameraHandle; + } + if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) { + return InvalidIrCameraHandle; + } + return ResultSuccess; +} + +Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry( + const Core::IrSensor::IrCameraHandle& camera_handle) { + ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id"); + return shared_memory->device[camera_handle.npad_id]; +} IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} { // clang-format off @@ -395,4 +561,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} { IRS_SYS::~IRS_SYS() = default; -} // namespace Service::HID +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index 361dc2213..2e6115c73 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h @@ -4,13 +4,19 @@ #pragma once #include "core/hid/hid_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" #include "core/hle/service/service.h" namespace Core { class System; } -namespace Service::HID { +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::IRS { class IRS final : public ServiceFramework<IRS> { public: @@ -18,234 +24,19 @@ public: ~IRS() override; private: - // This is nn::irsensor::IrCameraStatus - enum IrCameraStatus : u32 { - Available, - Unsupported, - Unconnected, - }; - - // This is nn::irsensor::IrCameraInternalStatus - enum IrCameraInternalStatus : u32 { - Stopped, - FirmwareUpdateNeeded, - Unkown2, - Unkown3, - Unkown4, - FirmwareVersionRequested, - FirmwareVersionIsInvalid, - Ready, - Setting, - }; - - // This is nn::irsensor::detail::StatusManager::IrSensorMode - enum IrSensorMode : u64 { - None, - MomentProcessor, - ClusteringProcessor, - ImageTransferProcessor, - PointingProcessorMarker, - TeraPluginProcessor, - IrLedProcessor, - }; - - // This is nn::irsensor::ImageProcessorStatus - enum ImageProcessorStatus : u8 { - stopped, - running, - }; - - // This is nn::irsensor::ImageTransferProcessorFormat - enum ImageTransferProcessorFormat : u8 { - Size320x240, - Size160x120, - Size80x60, - Size40x30, - Size20x15, - }; - - // This is nn::irsensor::AdaptiveClusteringMode - enum AdaptiveClusteringMode : u8 { - StaticFov, - DynamicFov, - }; - - // This is nn::irsensor::AdaptiveClusteringTargetDistance - enum AdaptiveClusteringTargetDistance : u8 { - Near, - Middle, - Far, - }; - - // This is nn::irsensor::IrsHandAnalysisMode - enum IrsHandAnalysisMode : u8 { - Silhouette, - Image, - SilhoueteAndImage, - SilhuetteOnly, - }; - - // This is nn::irsensor::IrSensorFunctionLevel - enum IrSensorFunctionLevel : u8 { - unknown0, - unknown1, - unknown2, - unknown3, - unknown4, - }; - - // This is nn::irsensor::IrCameraHandle - struct IrCameraHandle { - u8 npad_id{}; - Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None}; - INSERT_PADDING_BYTES(2); - }; - static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size"); - - struct IrsRect { - s16 x; - s16 y; - s16 width; - s16 height; + // This is nn::irsensor::detail::AruidFormat + struct AruidFormat { + u64 sensor_aruid; + u64 sensor_aruid_status; }; + static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size"); - // This is nn::irsensor::PackedMcuVersion - struct PackedMcuVersion { - u16 major; - u16 minor; + // This is nn::irsensor::detail::StatusManager + struct StatusManager { + std::array<Core::IrSensor::DeviceFormat, 9> device; + std::array<AruidFormat, 5> aruid; }; - static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); - - // This is nn::irsensor::MomentProcessorConfig - struct MomentProcessorConfig { - u64 exposire_time; - u8 light_target; - u8 gain; - u8 is_negative_used; - INSERT_PADDING_BYTES(7); - IrsRect window_of_interest; - u8 preprocess; - u8 preprocess_intensity_threshold; - INSERT_PADDING_BYTES(5); - }; - static_assert(sizeof(MomentProcessorConfig) == 0x28, - "MomentProcessorConfig is an invalid size"); - - // This is nn::irsensor::PackedMomentProcessorConfig - struct PackedMomentProcessorConfig { - u64 exposire_time; - u8 light_target; - u8 gain; - u8 is_negative_used; - INSERT_PADDING_BYTES(5); - IrsRect window_of_interest; - PackedMcuVersion required_mcu_version; - u8 preprocess; - u8 preprocess_intensity_threshold; - INSERT_PADDING_BYTES(2); - }; - static_assert(sizeof(PackedMomentProcessorConfig) == 0x20, - "PackedMomentProcessorConfig is an invalid size"); - - // This is nn::irsensor::ClusteringProcessorConfig - struct ClusteringProcessorConfig { - u64 exposire_time; - u32 light_target; - u32 gain; - u8 is_negative_used; - INSERT_PADDING_BYTES(7); - IrsRect window_of_interest; - u32 pixel_count_min; - u32 pixel_count_max; - u32 object_intensity_min; - u8 is_external_light_filter_enabled; - INSERT_PADDING_BYTES(3); - }; - static_assert(sizeof(ClusteringProcessorConfig) == 0x30, - "ClusteringProcessorConfig is an invalid size"); - - // This is nn::irsensor::PackedClusteringProcessorConfig - struct PackedClusteringProcessorConfig { - u64 exposire_time; - u8 light_target; - u8 gain; - u8 is_negative_used; - INSERT_PADDING_BYTES(5); - IrsRect window_of_interest; - PackedMcuVersion required_mcu_version; - u32 pixel_count_min; - u32 pixel_count_max; - u32 object_intensity_min; - u8 is_external_light_filter_enabled; - INSERT_PADDING_BYTES(2); - }; - static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30, - "PackedClusteringProcessorConfig is an invalid size"); - - // This is nn::irsensor::PackedImageTransferProcessorConfig - struct PackedImageTransferProcessorConfig { - u64 exposire_time; - u8 light_target; - u8 gain; - u8 is_negative_used; - INSERT_PADDING_BYTES(5); - PackedMcuVersion required_mcu_version; - u8 format; - INSERT_PADDING_BYTES(3); - }; - static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18, - "PackedImageTransferProcessorConfig is an invalid size"); - - // This is nn::irsensor::PackedTeraPluginProcessorConfig - struct PackedTeraPluginProcessorConfig { - PackedMcuVersion required_mcu_version; - u8 mode; - INSERT_PADDING_BYTES(3); - }; - static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8, - "PackedTeraPluginProcessorConfig is an invalid size"); - - // This is nn::irsensor::PackedPointingProcessorConfig - struct PackedPointingProcessorConfig { - IrsRect window_of_interest; - PackedMcuVersion required_mcu_version; - }; - static_assert(sizeof(PackedPointingProcessorConfig) == 0xC, - "PackedPointingProcessorConfig is an invalid size"); - - // This is nn::irsensor::PackedFunctionLevel - struct PackedFunctionLevel { - IrSensorFunctionLevel function_level; - INSERT_PADDING_BYTES(3); - }; - static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size"); - - // This is nn::irsensor::PackedImageTransferProcessorExConfig - struct PackedImageTransferProcessorExConfig { - u64 exposire_time; - u8 light_target; - u8 gain; - u8 is_negative_used; - INSERT_PADDING_BYTES(5); - PackedMcuVersion required_mcu_version; - ImageTransferProcessorFormat origin_format; - ImageTransferProcessorFormat trimming_format; - u16 trimming_start_x; - u16 trimming_start_y; - u8 is_external_light_filter_enabled; - INSERT_PADDING_BYTES(3); - }; - static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20, - "PackedImageTransferProcessorExConfig is an invalid size"); - - // This is nn::irsensor::PackedIrLedProcessorConfig - struct PackedIrLedProcessorConfig { - PackedMcuVersion required_mcu_version; - u8 light_target; - INSERT_PADDING_BYTES(3); - }; - static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8, - "PackedIrLedProcessorConfig is an invalid size"); + static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); void ActivateIrsensor(Kernel::HLERequestContext& ctx); void DeactivateIrsensor(Kernel::HLERequestContext& ctx); @@ -265,6 +56,56 @@ private: void RunIrLedProcessor(Kernel::HLERequestContext& ctx); void StopImageProcessorAsync(Kernel::HLERequestContext& ctx); void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); + + Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; + Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( + const Core::IrSensor::IrCameraHandle& camera_handle); + + template <typename T> + void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle, + Core::IrSensor::DeviceFormat& device_state) { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return; + } + processors[index] = std::make_unique<T>(device_state); + } + + template <typename T> + void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle, + Core::IrSensor::DeviceFormat& device_state) { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return; + } + processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index); + } + + template <typename T> + T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return static_cast<T&>(*processors[0]); + } + return static_cast<T&>(*processors[index]); + } + + template <typename T> + const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const { + const auto index = static_cast<std::size_t>(handle.npad_id); + if (index > sizeof(processors)) { + LOG_CRITICAL(Service_IRS, "Invalid index {}", index); + return static_cast<T&>(*processors[0]); + } + return static_cast<T&>(*processors[index]); + } + + Core::HID::EmulatedController* npad_device = nullptr; + StatusManager* shared_memory = nullptr; + std::array<std::unique_ptr<ProcessorBase>, 9> processors{}; }; class IRS_SYS final : public ServiceFramework<IRS_SYS> { @@ -273,4 +114,4 @@ public: ~IRS_SYS() override; }; -} // namespace Service::HID +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp new file mode 100644 index 000000000..6479af212 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/clustering_processor.h" + +namespace Service::IRS { +ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +ClusteringProcessor::~ClusteringProcessor() = default; + +void ClusteringProcessor::StartProcessor() {} + +void ClusteringProcessor::SuspendProcessor() {} + +void ClusteringProcessor::StopProcessor() {} + +void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.pixel_count_min = config.pixel_count_min; + current_config.pixel_count_max = config.pixel_count_max; + current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled; + current_config.object_intensity_min = config.object_intensity_min; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h new file mode 100644 index 000000000..6e2ba8846 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/clustering_processor.h @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class ClusteringProcessor final : public ProcessorBase { +public: + explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format); + ~ClusteringProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config); + +private: + // This is nn::irsensor::ClusteringProcessorConfig + struct ClusteringProcessorConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::IrsRect window_of_interest; + u32 pixel_count_min; + u32 pixel_count_max; + u32 object_intensity_min; + bool is_external_light_filter_enabled; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(ClusteringProcessorConfig) == 0x30, + "ClusteringProcessorConfig is an invalid size"); + + // This is nn::irsensor::AdaptiveClusteringProcessorConfig + struct AdaptiveClusteringProcessorConfig { + Core::IrSensor::AdaptiveClusteringMode mode; + Core::IrSensor::AdaptiveClusteringTargetDistance target_distance; + }; + static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8, + "AdaptiveClusteringProcessorConfig is an invalid size"); + + // This is nn::irsensor::ClusteringData + struct ClusteringData { + f32 average_intensity; + Core::IrSensor::IrsCentroid centroid; + u32 pixel_count; + Core::IrSensor::IrsRect bound; + }; + static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size"); + + // This is nn::irsensor::ClusteringProcessorState + struct ClusteringProcessorState { + s64 sampling_number; + u64 timestamp; + u8 object_count; + INSERT_PADDING_BYTES(3); + Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; + std::array<ClusteringData, 0x10> data; + }; + static_assert(sizeof(ClusteringProcessorState) == 0x198, + "ClusteringProcessorState is an invalid size"); + + ClusteringProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp new file mode 100644 index 000000000..98f0c579d --- /dev/null +++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp @@ -0,0 +1,150 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/irsensor/image_transfer_processor.h" + +namespace Service::IRS { +ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_, + Core::IrSensor::DeviceFormat& device_format, + std::size_t npad_index) + : device{device_format} { + npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); + + Core::HID::ControllerUpdateCallback engine_callback{ + .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, + .is_npad_service = true, + }; + callback_key = npad_device->SetCallback(engine_callback); + + device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +ImageTransferProcessor::~ImageTransferProcessor() { + npad_device->DeleteCallback(callback_key); +}; + +void ImageTransferProcessor::StartProcessor() { + is_active = true; + device.camera_status = Core::IrSensor::IrCameraStatus::Available; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; + processor_state.sampling_number = 0; + processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; +} + +void ImageTransferProcessor::SuspendProcessor() {} + +void ImageTransferProcessor::StopProcessor() {} + +void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { + if (type != Core::HID::ControllerTriggerType::IrSensor) { + return; + } + if (!is_transfer_memory_set) { + return; + } + + const auto camera_data = npad_device->GetCamera(); + + // This indicates how much ambient light is precent + processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; + processor_state.sampling_number = camera_data.sample; + + if (camera_data.format != current_config.origin_format) { + LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format, + current_config.origin_format); + memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); + return; + } + + if (current_config.origin_format > current_config.trimming_format) { + LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}", + current_config.origin_format, current_config.trimming_format); + memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); + return; + } + + std::vector<u8> window_data{}; + const auto origin_width = GetDataWidth(current_config.origin_format); + const auto origin_height = GetDataHeight(current_config.origin_format); + const auto trimming_width = GetDataWidth(current_config.trimming_format); + const auto trimming_height = GetDataHeight(current_config.trimming_format); + window_data.resize(GetDataSize(current_config.trimming_format)); + + if (trimming_width + current_config.trimming_start_x > origin_width || + trimming_height + current_config.trimming_start_y > origin_height) { + LOG_WARNING(Service_IRS, + "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})", + current_config.trimming_start_x, current_config.trimming_start_y, + trimming_width, trimming_height, origin_width, origin_height); + memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); + return; + } + + for (std::size_t y = 0; y < trimming_height; y++) { + for (std::size_t x = 0; x < trimming_width; x++) { + const std::size_t window_index = (y * trimming_width) + x; + const std::size_t origin_index = + ((y + current_config.trimming_start_y) * origin_width) + x + + current_config.trimming_start_x; + window_data[window_index] = camera_data.data[origin_index]; + } + } + + memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format)); + + if (!IsProcessorActive()) { + StartProcessor(); + } +} + +void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.origin_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); + current_config.trimming_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); + current_config.trimming_start_x = 0; + current_config.trimming_start_y = 0; + + npad_device->SetCameraFormat(current_config.origin_format); +} + +void ImageTransferProcessor::SetConfig( + Core::IrSensor::PackedImageTransferProcessorExConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.origin_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format); + current_config.trimming_format = + static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format); + current_config.trimming_start_x = config.trimming_start_x; + current_config.trimming_start_y = config.trimming_start_y; + + npad_device->SetCameraFormat(current_config.origin_format); +} + +void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) { + is_transfer_memory_set = true; + transfer_memory = t_mem; +} + +Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( + std::vector<u8>& data) const { + const auto size = GetDataSize(current_config.trimming_format); + data.resize(size); + memcpy(data.data(), transfer_memory, size); + return processor_state; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.h b/src/core/hle/service/hid/irsensor/image_transfer_processor.h new file mode 100644 index 000000000..393df492d --- /dev/null +++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.h @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Core::HID { +class EmulatedController; +} // namespace Core::HID + +namespace Service::IRS { +class ImageTransferProcessor final : public ProcessorBase { +public: + explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_, + Core::IrSensor::DeviceFormat& device_format, + std::size_t npad_index); + ~ImageTransferProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config); + void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config); + + // Transfer memory where the image data will be stored + void SetTransferMemoryPointer(u8* t_mem); + + Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; + +private: + // This is nn::irsensor::ImageTransferProcessorConfig + struct ImageTransferProcessorConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::ImageTransferProcessorFormat format; + }; + static_assert(sizeof(ImageTransferProcessorConfig) == 0x20, + "ImageTransferProcessorConfig is an invalid size"); + + // This is nn::irsensor::ImageTransferProcessorExConfig + struct ImageTransferProcessorExConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::ImageTransferProcessorFormat origin_format; + Core::IrSensor::ImageTransferProcessorFormat trimming_format; + u16 trimming_start_x; + u16 trimming_start_y; + bool is_external_light_filter_enabled; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28, + "ImageTransferProcessorExConfig is an invalid size"); + + void OnControllerUpdate(Core::HID::ControllerTriggerType type); + + ImageTransferProcessorExConfig current_config{}; + Core::IrSensor::ImageTransferProcessorState processor_state{}; + Core::IrSensor::DeviceFormat& device; + Core::HID::EmulatedController* npad_device; + int callback_key{}; + + u8* transfer_memory = nullptr; + bool is_transfer_memory_set = false; +}; +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.cpp b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp new file mode 100644 index 000000000..8e6dd99e4 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/ir_led_processor.h" + +namespace Service::IRS { +IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +IrLedProcessor::~IrLedProcessor() = default; + +void IrLedProcessor::StartProcessor() {} + +void IrLedProcessor::SuspendProcessor() {} + +void IrLedProcessor::StopProcessor() {} + +void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) { + current_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.light_target); +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.h b/src/core/hle/service/hid/irsensor/ir_led_processor.h new file mode 100644 index 000000000..c3d8693c9 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/ir_led_processor.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class IrLedProcessor final : public ProcessorBase { +public: + explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format); + ~IrLedProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config); + +private: + // This is nn::irsensor::IrLedProcessorConfig + struct IrLedProcessorConfig { + Core::IrSensor::CameraLightTarget light_target; + }; + static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size"); + + struct IrLedProcessorState { + s64 sampling_number; + u64 timestamp; + std::array<u8, 0x8> data; + }; + static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size"); + + IrLedProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp new file mode 100644 index 000000000..dbaca420a --- /dev/null +++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/moment_processor.h" + +namespace Service::IRS { +MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +MomentProcessor::~MomentProcessor() = default; + +void MomentProcessor::StartProcessor() {} + +void MomentProcessor::SuspendProcessor() {} + +void MomentProcessor::StopProcessor() {} + +void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { + current_config.camera_config.exposure_time = config.camera_config.exposure_time; + current_config.camera_config.gain = config.camera_config.gain; + current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; + current_config.camera_config.light_target = + static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); + current_config.window_of_interest = config.window_of_interest; + current_config.preprocess = + static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); + current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h new file mode 100644 index 000000000..d4bd22e0f --- /dev/null +++ b/src/core/hle/service/hid/irsensor/moment_processor.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class MomentProcessor final : public ProcessorBase { +public: + explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format); + ~MomentProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); + +private: + // This is nn::irsensor::MomentProcessorConfig + struct MomentProcessorConfig { + Core::IrSensor::CameraConfig camera_config; + Core::IrSensor::IrsRect window_of_interest; + Core::IrSensor::MomentProcessorPreprocess preprocess; + u32 preprocess_intensity_threshold; + }; + static_assert(sizeof(MomentProcessorConfig) == 0x28, + "MomentProcessorConfig is an invalid size"); + + // This is nn::irsensor::MomentStatistic + struct MomentStatistic { + f32 average_intensity; + Core::IrSensor::IrsCentroid centroid; + }; + static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size"); + + // This is nn::irsensor::MomentProcessorState + struct MomentProcessorState { + s64 sampling_number; + u64 timestamp; + Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; + INSERT_PADDING_BYTES(4); + std::array<MomentStatistic, 0x30> stadistic; + }; + static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); + + MomentProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.cpp b/src/core/hle/service/hid/irsensor/pointing_processor.cpp new file mode 100644 index 000000000..929f177fc --- /dev/null +++ b/src/core/hle/service/hid/irsensor/pointing_processor.cpp @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/pointing_processor.h" + +namespace Service::IRS { +PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +PointingProcessor::~PointingProcessor() = default; + +void PointingProcessor::StartProcessor() {} + +void PointingProcessor::SuspendProcessor() {} + +void PointingProcessor::StopProcessor() {} + +void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) { + current_config.window_of_interest = config.window_of_interest; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h new file mode 100644 index 000000000..cf4930794 --- /dev/null +++ b/src/core/hle/service/hid/irsensor/pointing_processor.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class PointingProcessor final : public ProcessorBase { +public: + explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format); + ~PointingProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config); + +private: + // This is nn::irsensor::PointingProcessorConfig + struct PointingProcessorConfig { + Core::IrSensor::IrsRect window_of_interest; + }; + static_assert(sizeof(PointingProcessorConfig) == 0x8, + "PointingProcessorConfig is an invalid size"); + + struct PointingProcessorMarkerData { + u8 pointing_status; + INSERT_PADDING_BYTES(3); + u32 unknown; + float unkown_float1; + float position_x; + float position_y; + float unkown_float2; + Core::IrSensor::IrsRect window_of_interest; + }; + static_assert(sizeof(PointingProcessorMarkerData) == 0x20, + "PointingProcessorMarkerData is an invalid size"); + + struct PointingProcessorMarkerState { + s64 sampling_number; + u64 timestamp; + std::array<PointingProcessorMarkerData, 0x3> data; + }; + static_assert(sizeof(PointingProcessorMarkerState) == 0x70, + "PointingProcessorMarkerState is an invalid size"); + + PointingProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/processor_base.cpp b/src/core/hle/service/hid/irsensor/processor_base.cpp new file mode 100644 index 000000000..4d43ca17a --- /dev/null +++ b/src/core/hle/service/hid/irsensor/processor_base.cpp @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { + +ProcessorBase::ProcessorBase() {} +ProcessorBase::~ProcessorBase() = default; + +bool ProcessorBase::IsProcessorActive() const { + return is_active; +} + +std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const { + switch (format) { + case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: + return 320 * 240; + case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: + return 160 * 120; + case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: + return 80 * 60; + case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: + return 40 * 30; + case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: + return 20 * 15; + default: + return 0; + } +} + +std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const { + switch (format) { + case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: + return 320; + case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: + return 160; + case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: + return 80; + case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: + return 40; + case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: + return 20; + default: + return 0; + } +} + +std::size_t ProcessorBase::GetDataHeight( + Core::IrSensor::ImageTransferProcessorFormat format) const { + switch (format) { + case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: + return 240; + case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: + return 120; + case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: + return 60; + case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: + return 30; + case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: + return 15; + default: + return 0; + } +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/processor_base.h b/src/core/hle/service/hid/irsensor/processor_base.h new file mode 100644 index 000000000..bc0d2977b --- /dev/null +++ b/src/core/hle/service/hid/irsensor/processor_base.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hid/irs_types.h" + +namespace Service::IRS { +class ProcessorBase { +public: + explicit ProcessorBase(); + virtual ~ProcessorBase(); + + virtual void StartProcessor() = 0; + virtual void SuspendProcessor() = 0; + virtual void StopProcessor() = 0; + + bool IsProcessorActive() const; + +protected: + /// Returns the number of bytes the image uses + std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const; + + /// Returns the width of the image + std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const; + + /// Returns the height of the image + std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const; + + bool is_active{false}; +}; +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp new file mode 100644 index 000000000..e691c840a --- /dev/null +++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/irsensor/tera_plugin_processor.h" + +namespace Service::IRS { +TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format) + : device(device_format) { + device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor; + device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; + device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; +} + +TeraPluginProcessor::~TeraPluginProcessor() = default; + +void TeraPluginProcessor::StartProcessor() {} + +void TeraPluginProcessor::SuspendProcessor() {} + +void TeraPluginProcessor::StopProcessor() {} + +void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) { + current_config.mode = config.mode; + current_config.unknown_1 = config.unknown_1; + current_config.unknown_2 = config.unknown_2; + current_config.unknown_3 = config.unknown_3; +} + +} // namespace Service::IRS diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.h b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h new file mode 100644 index 000000000..bbea7ed0b --- /dev/null +++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/bit_field.h" +#include "common/common_types.h" +#include "core/hid/irs_types.h" +#include "core/hle/service/hid/irsensor/processor_base.h" + +namespace Service::IRS { +class TeraPluginProcessor final : public ProcessorBase { +public: + explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format); + ~TeraPluginProcessor() override; + + // Called when the processor is initialized + void StartProcessor() override; + + // Called when the processor is suspended + void SuspendProcessor() override; + + // Called when the processor is stopped + void StopProcessor() override; + + // Sets config parameters of the camera + void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config); + +private: + // This is nn::irsensor::TeraPluginProcessorConfig + struct TeraPluginProcessorConfig { + u8 mode; + u8 unknown_1; + u8 unknown_2; + u8 unknown_3; + }; + static_assert(sizeof(TeraPluginProcessorConfig) == 0x4, + "TeraPluginProcessorConfig is an invalid size"); + + struct TeraPluginProcessorState { + s64 sampling_number; + u64 timestamp; + Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; + std::array<u8, 0x12c> data; + }; + static_assert(sizeof(TeraPluginProcessorState) == 0x140, + "TeraPluginProcessorState is an invalid size"); + + TeraPluginProcessorConfig current_config{}; + Core::IrSensor::DeviceFormat& device; +}; + +} // namespace Service::IRS diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 0310ce883..7055ea93e 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -30,6 +30,19 @@ enum class RequestState : u32 { Connected = 3, }; +enum class InternetConnectionType : u8 { + WiFi = 1, + Ethernet = 2, +}; + +enum class InternetConnectionStatus : u8 { + ConnectingUnknown1, + ConnectingUnknown2, + ConnectingUnknown3, + ConnectingUnknown4, + Connected, +}; + struct IpAddressSetting { bool is_automatic{}; Network::IPv4Address current_address{}; @@ -271,6 +284,7 @@ private: rb.Push(ResultSuccess); rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid } + void CreateScanRequest(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NIFM, "called"); @@ -279,6 +293,7 @@ private: rb.Push(ResultSuccess); rb.PushIpcInterface<IScanRequest>(system); } + void CreateRequest(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NIFM, "called"); @@ -287,6 +302,7 @@ private: rb.Push(ResultSuccess); rb.PushIpcInterface<IRequest>(system); } + void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); @@ -335,12 +351,14 @@ private: IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } + void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } + void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); @@ -354,6 +372,7 @@ private: rb.Push(ResultSuccess); rb.PushRaw(*ipv4); } + void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NIFM, "called"); @@ -369,6 +388,7 @@ private: rb.PushIpcInterface<INetworkProfile>(system); rb.PushRaw<u128>(uuid); } + void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); @@ -405,6 +425,7 @@ private: rb.Push(ResultSuccess); rb.PushRaw<IpConfigInfo>(ip_config_info); } + void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); @@ -412,6 +433,24 @@ private: rb.Push(ResultSuccess); rb.Push<u8>(0); } + + void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + struct Output { + InternetConnectionType type{InternetConnectionType::WiFi}; + u8 wifi_strength{3}; + InternetConnectionStatus state{InternetConnectionStatus::Connected}; + }; + static_assert(sizeof(Output) == 0x3, "Output has incorrect size."); + + constexpr Output out{}; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(out); + } + void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); @@ -423,6 +462,7 @@ private: rb.Push<u8>(0); } } + void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); @@ -456,7 +496,7 @@ IGeneralService::IGeneralService(Core::System& system_) {15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"}, {16, nullptr, "SetWirelessCommunicationEnabled"}, {17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"}, - {18, nullptr, "GetInternetConnectionStatus"}, + {18, &IGeneralService::GetInternetConnectionStatus, "GetInternetConnectionStatus"}, {19, nullptr, "SetEthernetCommunicationEnabled"}, {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"}, {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"}, diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h index 21aaa40cd..157333ff8 100644 --- a/src/core/hle/service/nvflinger/binder.h +++ b/src/core/hle/service/nvflinger/binder.h @@ -34,6 +34,7 @@ enum class TransactionId { class IBinder { public: + virtual ~IBinder() = default; virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) = 0; virtual Kernel::KReadableEvent& GetNativeHandle() = 0; diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 5f69c8c2c..5574269eb 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -287,9 +287,21 @@ s64 NVFlinger::GetNextTicks() const { static constexpr s64 max_hertz = 120LL; const auto& settings = Settings::values; - const bool unlocked_fps = settings.disable_fps_limit.GetValue(); - const s64 fps_cap = unlocked_fps ? static_cast<s64>(settings.fps_cap.GetValue()) : 1; - return (1000000000 * (1LL << swap_interval)) / (max_hertz * fps_cap); + auto speed_scale = 1.f; + if (settings.use_multi_core.GetValue()) { + if (settings.use_speed_limit.GetValue()) { + // Scales the speed based on speed_limit setting on MC. SC is handled by + // SpeedLimiter::DoSpeedLimiting. + speed_scale = 100.f / settings.speed_limit.GetValue(); + } else { + // Run at unlocked framerate. + speed_scale = 0.01f; + } + } + + const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz); + + return static_cast<s64>(speed_scale * static_cast<float>(next_ticks)); } } // namespace Service::NVFlinger diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp index 9e0eb6ac0..2c31e9485 100644 --- a/src/core/hle/service/ptm/psm.cpp +++ b/src/core/hle/service/ptm/psm.cpp @@ -9,10 +9,8 @@ #include "core/hle/kernel/k_event.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/ptm/psm.h" -#include "core/hle/service/service.h" -#include "core/hle/service/sm/sm.h" -namespace Service::PSM { +namespace Service::PTM { class IPsmSession final : public ServiceFramework<IPsmSession> { public: @@ -57,7 +55,7 @@ public: private: void BindStateChangeEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); + LOG_DEBUG(Service_PTM, "called"); should_signal = true; @@ -67,7 +65,7 @@ private: } void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); + LOG_DEBUG(Service_PTM, "called"); should_signal = false; @@ -78,7 +76,7 @@ private: void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto state = rp.Pop<bool>(); - LOG_DEBUG(Service_PSM, "called, state={}", state); + LOG_DEBUG(Service_PTM, "called, state={}", state); should_signal_charger_type = state; @@ -89,7 +87,7 @@ private: void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto state = rp.Pop<bool>(); - LOG_DEBUG(Service_PSM, "called, state={}", state); + LOG_DEBUG(Service_PTM, "called, state={}", state); should_signal_power_supply = state; @@ -100,7 +98,7 @@ private: void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto state = rp.Pop<bool>(); - LOG_DEBUG(Service_PSM, "called, state={}", state); + LOG_DEBUG(Service_PTM, "called, state={}", state); should_signal_battery_voltage = state; @@ -117,76 +115,58 @@ private: Kernel::KEvent* state_change_event; }; -class PSM final : public ServiceFramework<PSM> { -public: - explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"}, - {1, &PSM::GetChargerType, "GetChargerType"}, - {2, nullptr, "EnableBatteryCharging"}, - {3, nullptr, "DisableBatteryCharging"}, - {4, nullptr, "IsBatteryChargingEnabled"}, - {5, nullptr, "AcquireControllerPowerSupply"}, - {6, nullptr, "ReleaseControllerPowerSupply"}, - {7, &PSM::OpenSession, "OpenSession"}, - {8, nullptr, "EnableEnoughPowerChargeEmulation"}, - {9, nullptr, "DisableEnoughPowerChargeEmulation"}, - {10, nullptr, "EnableFastBatteryCharging"}, - {11, nullptr, "DisableFastBatteryCharging"}, - {12, nullptr, "GetBatteryVoltageState"}, - {13, nullptr, "GetRawBatteryChargePercentage"}, - {14, nullptr, "IsEnoughPowerSupplied"}, - {15, nullptr, "GetBatteryAgePercentage"}, - {16, nullptr, "GetBatteryChargeInfoEvent"}, - {17, nullptr, "GetBatteryChargeInfoFields"}, - {18, nullptr, "GetBatteryChargeCalibratedEvent"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - - ~PSM() override = default; - -private: - void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); +PSM::PSM(Core::System& system_) : ServiceFramework{system_, "psm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"}, + {1, &PSM::GetChargerType, "GetChargerType"}, + {2, nullptr, "EnableBatteryCharging"}, + {3, nullptr, "DisableBatteryCharging"}, + {4, nullptr, "IsBatteryChargingEnabled"}, + {5, nullptr, "AcquireControllerPowerSupply"}, + {6, nullptr, "ReleaseControllerPowerSupply"}, + {7, &PSM::OpenSession, "OpenSession"}, + {8, nullptr, "EnableEnoughPowerChargeEmulation"}, + {9, nullptr, "DisableEnoughPowerChargeEmulation"}, + {10, nullptr, "EnableFastBatteryCharging"}, + {11, nullptr, "DisableFastBatteryCharging"}, + {12, nullptr, "GetBatteryVoltageState"}, + {13, nullptr, "GetRawBatteryChargePercentage"}, + {14, nullptr, "IsEnoughPowerSupplied"}, + {15, nullptr, "GetBatteryAgePercentage"}, + {16, nullptr, "GetBatteryChargeInfoEvent"}, + {17, nullptr, "GetBatteryChargeInfoFields"}, + {18, nullptr, "GetBatteryChargeCalibratedEvent"}, + }; + // clang-format on - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(battery_charge_percentage); - } + RegisterHandlers(functions); +} - void GetChargerType(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); +PSM::~PSM() = default; - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(charger_type); - } +void PSM::GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "called"); - void OpenSession(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PSM, "called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(battery_charge_percentage); +} - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IPsmSession>(system); - } +void PSM::GetChargerType(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "called"); - enum class ChargerType : u32 { - Unplugged = 0, - RegularCharger = 1, - LowPowerCharger = 2, - Unknown = 3, - }; + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(charger_type); +} - u32 battery_charge_percentage{100}; // 100% - ChargerType charger_type{ChargerType::RegularCharger}; -}; +void PSM::OpenSession(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PTM, "called"); -void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { - std::make_shared<PSM>(system)->InstallAsService(sm); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IPsmSession>(system); } -} // namespace Service::PSM +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h index 94a1044db..f674ba8bc 100644 --- a/src/core/hle/service/ptm/psm.h +++ b/src/core/hle/service/ptm/psm.h @@ -3,16 +3,29 @@ #pragma once -namespace Core { -class System; -} +#include "core/hle/service/service.h" -namespace Service::SM { -class ServiceManager; -} +namespace Service::PTM { -namespace Service::PSM { +class PSM final : public ServiceFramework<PSM> { +public: + explicit PSM(Core::System& system_); + ~PSM() override; -void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); +private: + enum class ChargerType : u32 { + Unplugged = 0, + RegularCharger = 1, + LowPowerCharger = 2, + Unknown = 3, + }; -} // namespace Service::PSM + void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx); + void GetChargerType(Kernel::HLERequestContext& ctx); + void OpenSession(Kernel::HLERequestContext& ctx); + + u32 battery_charge_percentage{100}; + ChargerType charger_type{ChargerType::RegularCharger}; +}; + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp new file mode 100644 index 000000000..4bea995c6 --- /dev/null +++ b/src/core/hle/service/ptm/ptm.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <memory> + +#include "core/core.h" +#include "core/hle/service/ptm/psm.h" +#include "core/hle/service/ptm/ptm.h" +#include "core/hle/service/ptm/ts.h" + +namespace Service::PTM { + +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { + std::make_shared<PSM>(system)->InstallAsService(sm); + std::make_shared<TS>(system)->InstallAsService(sm); +} + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h new file mode 100644 index 000000000..06224a24e --- /dev/null +++ b/src/core/hle/service/ptm/ptm.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +namespace Core { +class System; +} + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::PTM { + +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp new file mode 100644 index 000000000..65c3f135f --- /dev/null +++ b/src/core/hle/service/ptm/ts.cpp @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <memory> + +#include "core/core.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/ptm/ts.h" + +namespace Service::PTM { + +TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetTemperatureRange"}, + {1, &TS::GetTemperature, "GetTemperature"}, + {2, nullptr, "SetMeasurementMode"}, + {3, nullptr, "GetTemperatureMilliC"}, + {4, nullptr, "OpenSession"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +TS::~TS() = default; + +void TS::GetTemperature(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto location{rp.PopEnum<Location>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location); + + const s32 temperature = location == Location::Internal ? 35 : 20; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(temperature); +} + +} // namespace Service::PTM diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h new file mode 100644 index 000000000..39a734ef7 --- /dev/null +++ b/src/core/hle/service/ptm/ts.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/service.h" + +namespace Service::PTM { + +class TS final : public ServiceFramework<TS> { +public: + explicit TS(Core::System& system_); + ~TS() override; + +private: + enum class Location : u8 { + Internal, + External, + }; + + void GetTemperature(Kernel::HLERequestContext& ctx); +}; + +} // namespace Service::PTM diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 318009e4f..c64291e7f 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -58,7 +58,7 @@ #include "core/hle/service/pm/pm.h" #include "core/hle/service/prepo/prepo.h" #include "core/hle/service/psc/psc.h" -#include "core/hle/service/ptm/psm.h" +#include "core/hle/service/ptm/ptm.h" #include "core/hle/service/service.h" #include "core/hle/service/set/settings.h" #include "core/hle/service/sm/sm.h" @@ -287,7 +287,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system PlayReport::InstallInterfaces(*sm, system); PM::InstallInterfaces(system); PSC::InstallInterfaces(*sm, system); - PSM::InstallInterfaces(*sm, system); + PTM::InstallInterfaces(*sm, system); Set::InstallInterfaces(*sm, system); Sockets::InstallInterfaces(*sm, system); SPL::InstallInterfaces(*sm, system); diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 5114b8be2..3e9dc4a13 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -720,7 +720,25 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) if (!IsFileDescriptorValid(fd)) { return {-1, Errno::BADF}; } - return Translate(file_descriptors[fd]->socket->Recv(flags, message)); + + FileDescriptor& descriptor = *file_descriptors[fd]; + + // Apply flags + if ((flags & FLAG_MSG_DONTWAIT) != 0) { + flags &= ~FLAG_MSG_DONTWAIT; + if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { + descriptor.socket->SetNonBlock(true); + } + } + + const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message)); + + // Restore original state + if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { + descriptor.socket->SetNonBlock(false); + } + + return {ret, bsd_errno}; } std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, |