Compare commits

..

8 Commits

Author SHA1 Message Date
lizzie 47aa1ac0c8 2026-09-09 16:05:11
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-11 14:42:18 +00:00
lizzie 04a0a48ac4 2026-09-07 23:31:06
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-11 14:42:18 +00:00
lizzie 6d152c459d 2026-09-07 23:30:42
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-11 14:42:18 +00:00
lizzie aa3ffd6514 fix span shit 2026-09-11 14:42:18 +00:00
lizzie d9e8eecc3b [audio_core] remove dangling Core::System& references
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-11 14:42:16 +00:00
lizzie 8a22f1845b [hle] Enforce max_sessions, add bpc:ams service (#4394)
As per switchbrew, as per atmosphere, and some poking around.

bpc:ams stubbed for now.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4394
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-11 21:38:52 +02:00
xbzk 301da63a15 [audio] hint openslES as audio driver on android to fix mute screen recording issue (#4397)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
After audio backend migration some android devices screen captures became mute.
Liz instructed to enable openSL ES via some SDL hint.
Found the proper one in https://wiki.libsdl.org/SDL3/SDL_HINT_AUDIO_DRIVER.
Added it restricted to android.

UPDATE:
Got myself wondering how things work on SDL after using that hint, and checked that one must provide a list of drivers "opensles,aaudio,..." to be attempted.
Afraid of some devices failing on openSL ES, and to avoid hand providing entire list, i've added fallback logic so entire SDL's driver list can be tried just in case.
Default list in https://github.com/libsdl-org/SDL/blob/release-3.4.14/src/audio/SDL_audio.c.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4397
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-11 21:38:12 +02:00
lizzie c95ad020fb [gpu] Fix infinite hangup/freezes at shutdown/reset (#4395)
Thread may be destroyed at dtor(), but it hasn't fully shut down, so before notify shutdown, request immediate stop (effective immediately).

Like the issue was that the thread didn't want to stop running, thus it would hang, it would also reference objects which were being destroyed without waiting for them to actually be destroyed
So it would reference invalid data
This PR tells the thread: "hey, STOP now, and DESTROY yourself"
So all of that should be avoided

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4395
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-11 15:29:52 +02:00
44 changed files with 1146 additions and 315 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
namespace AudioCore { namespace AudioCore {
AudioCore::AudioCore(Core::System& system) { AudioCore::AudioCore(Core::System& system) {
audio_manager.emplace(); audio_manager.emplace(system);
CreateSinks(); CreateSinks();
// Must be created after the sinks // Must be created after the sinks
adsp.emplace(system, *output_sink); adsp.emplace(system, *output_sink);
+12 -15
View File
@@ -15,12 +15,12 @@
namespace AudioCore::AudioIn { namespace AudioCore::AudioIn {
Manager::Manager(Core::System& system_) : system{system_} { Manager::Manager(Core::System& system) {
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
num_free_sessions = MaxInSessions; num_free_sessions = MaxInSessions;
} }
Result Manager::AcquireSessionId(size_t& session_id) { Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
if (num_free_sessions == 0) { if (num_free_sessions == 0) {
LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more"); LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
@@ -31,7 +31,7 @@ Result Manager::AcquireSessionId(size_t& session_id) {
return ResultSuccess; return ResultSuccess;
} }
void Manager::ReleaseSessionId(const size_t session_id) { void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id); LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id);
session_ids[free_session_id] = session_id; session_ids[free_session_id] = session_id;
@@ -41,21 +41,20 @@ void Manager::ReleaseSessionId(const size_t session_id) {
applet_resource_user_ids[session_id] = 0; applet_resource_user_ids[session_id] = 0;
} }
Result Manager::LinkToManager() { Result Manager::LinkToManager(Core::System& system) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
if (!linked_to_manager) { if (!linked_to_manager) {
system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this)); system.AudioCore().GetAudioManager().SetInManager(this, &Manager::BufferReleaseAndRegister);
linked_to_manager = true; linked_to_manager = true;
} }
return ResultSuccess; return ResultSuccess;
} }
void Manager::Start() { void Manager::Start(Core::System& system) {
if (sessions_started) { if (sessions_started) {
return; return;
} }
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
for (auto& session : sessions) { for (auto& session : sessions) {
if (session) { if (session) {
@@ -66,21 +65,19 @@ void Manager::Start() {
sessions_started = true; sessions_started = true;
} }
void Manager::BufferReleaseAndRegister() { void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept {
std::scoped_lock l{mutex}; Manager* this_ = (Manager*)data;
for (auto& session : sessions) { std::scoped_lock l{this_->mutex};
for (auto& session : this_->sessions) {
if (session != nullptr) { if (session != nullptr) {
session->ReleaseAndRegisterBuffers(); session->ReleaseAndRegisterBuffers();
} }
} }
} }
u32 Manager::GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, u32 Manager::GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, [[maybe_unused]] const bool filter) {
[[maybe_unused]] const bool filter) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LinkToManager(system);
LinkToManager();
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)}; auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
if (!input_devices.empty() && !names.empty()) { if (!input_devices.empty() && !names.empty()) {
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac"); names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
+10 -11
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -30,31 +33,29 @@ public:
* @param session_id - Output session_id. * @param session_id - Output session_id.
* @return Result code. * @return Result code.
*/ */
Result AcquireSessionId(size_t& session_id); Result AcquireSessionId(Core::System& system, size_t& session_id);
/** /**
* Release a session id on close. * Release a session id on close.
* *
* @param session_id - Session id to free. * @param session_id - Session id to free.
*/ */
void ReleaseSessionId(size_t session_id); void ReleaseSessionId(Core::System& system, const size_t session_id);
/** /**
* Link the audio in manager to the main audio manager. * Link the audio in manager to the main audio manager.
* *
* @return Result code. * @return Result code.
*/ */
Result LinkToManager(); Result LinkToManager(Core::System& system);
/** /**
* Start the audio in manager. * Start the audio in manager.
*/ */
void Start(); void Start(Core::System& system);
/** /// @brief Callback function, called by the audio manager when the audio in event is signalled.
* Callback function, called by the audio manager when the audio in event is signalled. static void BufferReleaseAndRegister(void *data, Core::System& system) noexcept;
*/
void BufferReleaseAndRegister();
/** /**
* Get a list of audio in device names. * Get a list of audio in device names.
@@ -64,10 +65,8 @@ public:
* *
* @return Number of names written. * @return Number of names written.
*/ */
u32 GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter); u32 GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
/// Core system
Core::System& system;
/// Array of session ids /// Array of session ids
std::array<size_t, MaxInSessions> session_ids{}; std::array<size_t, MaxInSessions> session_ids{};
/// Array of resource user ids /// Array of resource user ids
+7 -5
View File
@@ -11,8 +11,8 @@
namespace AudioCore { namespace AudioCore {
AudioManager::AudioManager() { AudioManager::AudioManager(Core::System& system) {
thread = std::jthread([this](std::stop_token stop_token) { thread = std::jthread([&](std::stop_token stop_token) {
Common::SetCurrentThreadName("AudioManager"); Common::SetCurrentThreadName("AudioManager");
std::unique_lock l{events.GetAudioEventLock()}; std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents(); events.ClearEvents();
@@ -25,7 +25,7 @@ AudioManager::AudioManager() {
const auto event_type = Event::Type(i); const auto event_type = Event::Type(i);
if (events.CheckAudioEventSet(event_type) || timed_out) { if (events.CheckAudioEventSet(event_type) || timed_out) {
if (buffer_events[i]) { if (buffer_events[i]) {
buffer_events[i](); buffer_events[i](buffer_data[i], system);
} }
} }
events.SetAudioEvent(event_type, false); events.SetAudioEvent(event_type, false);
@@ -42,12 +42,13 @@ void AudioManager::Shutdown() {
} }
} }
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) { Result AudioManager::SetOutManager(void *data, BufferEventFunc buffer_func) {
if (thread.joinable()) { if (thread.joinable()) {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)}; const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
if (buffer_events[index] == nullptr) { if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func); buffer_events[index] = std::move(buffer_func);
buffer_data[index] = data;
needs_update = true; needs_update = true;
events.SetAudioEvent(Event::Type::AudioOutManager, true); events.SetAudioEvent(Event::Type::AudioOutManager, true);
} }
@@ -56,12 +57,13 @@ Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
return Service::Audio::ResultOperationFailed; return Service::Audio::ResultOperationFailed;
} }
Result AudioManager::SetInManager(BufferEventFunc buffer_func) { Result AudioManager::SetInManager(void *data, BufferEventFunc buffer_func) {
if (thread.joinable()) { if (thread.joinable()) {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)}; const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
if (buffer_events[index] == nullptr) { if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func); buffer_events[index] = std::move(buffer_func);
buffer_data[index] = data;
needs_update = true; needs_update = true;
events.SetAudioEvent(Event::Type::AudioInManager, true); events.SetAudioEvent(Event::Type::AudioInManager, true);
} }
+16 -18
View File
@@ -16,6 +16,10 @@
#include "audio_core/audio_event.h" #include "audio_core/audio_event.h"
namespace Core {
class System;
}
union Result; union Result;
namespace AudioCore { namespace AudioCore {
@@ -34,31 +38,24 @@ namespace AudioCore {
* This is only used by audio in and audio out. * This is only used by audio in and audio out.
*/ */
class AudioManager { class AudioManager {
using BufferEventFunc = std::function<void()>; using BufferEventFunc = void (*)(void *data, Core::System& system) noexcept;
public: public:
explicit AudioManager(); explicit AudioManager(Core::System& system);
/** /**
* Shutdown the audio manager. * Shutdown the audio manager.
*/ */
void Shutdown(); void Shutdown();
/** /// Register the out manager, keeping a function to be called when the out event is signalled.
* Register the out manager, keeping a function to be called when the out event is signalled. /// @param buffer_func - Function to be called on signal.
* /// @return Result code.
* @param buffer_func - Function to be called on signal. Result SetOutManager(void *data, BufferEventFunc buffer_func);
* @return Result code.
*/
Result SetOutManager(BufferEventFunc buffer_func);
/** /// Register the in manager, keeping a function to be called when the in event is signalled.
* Register the in manager, keeping a function to be called when the in event is signalled. /// @param buffer_func - Function to be called on signal.
* /// @return Result code.
* @param buffer_func - Function to be called on signal. Result SetInManager(void *data, BufferEventFunc buffer_func);
* @return Result code.
*/
Result SetInManager(BufferEventFunc buffer_func);
/** /**
* Set an event to signalled, and signal the thread. * Set an event to signalled, and signal the thread.
@@ -73,8 +70,9 @@ private:
bool needs_update{}; bool needs_update{};
/// Events to be set and signalled /// Events to be set and signalled
Event events{}; Event events{};
/// Callbacks for each manager /// Callbacks (and user data) for each manager
std::array<BufferEventFunc, 3> buffer_events{}; std::array<BufferEventFunc, 3> buffer_events{};
std::array<void*, 3> buffer_data{};
/// General lock /// General lock
std::mutex lock{}; std::mutex lock{};
/// Main thread for waiting and callbacks /// Main thread for waiting and callbacks
+17 -25
View File
@@ -14,12 +14,12 @@
namespace AudioCore::AudioOut { namespace AudioCore::AudioOut {
Manager::Manager(Core::System& system_) : system{system_} { Manager::Manager(Core::System& system) {
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
num_free_sessions = MaxOutSessions; num_free_sessions = MaxOutSessions;
} }
Result Manager::AcquireSessionId(size_t& session_id) { Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
if (num_free_sessions == 0) { if (num_free_sessions == 0) {
LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more"); LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
@@ -30,7 +30,7 @@ Result Manager::AcquireSessionId(size_t& session_id) {
return ResultSuccess; return ResultSuccess;
} }
void Manager::ReleaseSessionId(const size_t session_id) { void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id); LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id);
session_ids[free_session_id] = session_id; session_ids[free_session_id] = session_id;
@@ -40,44 +40,36 @@ void Manager::ReleaseSessionId(const size_t session_id) {
applet_resource_user_ids[session_id] = 0; applet_resource_user_ids[session_id] = 0;
} }
Result Manager::LinkToManager() { Result Manager::LinkToManager(Core::System& system) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
if (!linked_to_manager) { if (!linked_to_manager) {
system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this)); system.AudioCore().GetAudioManager().SetOutManager(this, &Manager::BufferReleaseAndRegister);
linked_to_manager = true; linked_to_manager = true;
} }
return ResultSuccess; return ResultSuccess;
} }
void Manager::Start() { void Manager::Start(Core::System& system) {
if (sessions_started) { if (!sessions_started) {
return; std::scoped_lock l{mutex};
} for (auto& session : sessions) {
if (session) {
std::scoped_lock l{mutex}; session->StartSession();
for (auto& session : sessions) { }
if (session) {
session->StartSession();
} }
sessions_started = true;
} }
sessions_started = true;
} }
void Manager::BufferReleaseAndRegister() { void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept {
std::scoped_lock l{mutex}; Manager* this_ = (Manager*)data;
for (auto& session : sessions) { std::scoped_lock l{this_->mutex};
for (auto& session : this_->sessions) {
if (session != nullptr) { if (session != nullptr) {
session->ReleaseAndRegisterBuffers(); session->ReleaseAndRegisterBuffers();
} }
} }
} }
u32 Manager::GetAudioOutDeviceNames(
std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const {
names.emplace_back("DeviceOut");
return 1;
}
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+8 -15
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -29,42 +32,32 @@ public:
* @param session_id - Output session_id. * @param session_id - Output session_id.
* @return Result code. * @return Result code.
*/ */
Result AcquireSessionId(size_t& session_id); Result AcquireSessionId(Core::System& system, size_t& session_id);
/** /**
* Release a session id on close. * Release a session id on close.
* *
* @param session_id - Session id to free. * @param session_id - Session id to free.
*/ */
void ReleaseSessionId(size_t session_id); void ReleaseSessionId(Core::System& system, const size_t session_id);
/** /**
* Link this manager to the main audio manager. * Link this manager to the main audio manager.
* *
* @return Result code. * @return Result code.
*/ */
Result LinkToManager(); Result LinkToManager(Core::System& system);
/** /**
* Start the audio out manager. * Start the audio out manager.
*/ */
void Start(); void Start(Core::System& system);
/** /**
* Callback function, called by the audio manager when the audio out event is signalled. * Callback function, called by the audio manager when the audio out event is signalled.
*/ */
void BufferReleaseAndRegister(); static void BufferReleaseAndRegister(void* data, Core::System& system) noexcept;
/**
* Get a list of audio out device names.
*
* @param names - Output container to write names to.
* @return Number of names written.
*/
u32 GetAudioOutDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const;
/// Core system
Core::System& system;
/// Array of session ids /// Array of session ids
std::array<size_t, MaxOutSessions> session_ids{}; std::array<size_t, MaxOutSessions> session_ids{};
/// Array of resource user ids /// Array of resource user ids
+13 -10
View File
@@ -1,15 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/audio_render_manager.h" #include "audio_core/audio_render_manager.h"
#include "audio_core/common/audio_renderer_parameter.h" #include "audio_core/common/audio_renderer_parameter.h"
#include "audio_core/renderer/system_manager.h"
#include "audio_core/common/feature_support.h" #include "audio_core/common/feature_support.h"
#include "core/core.h" #include "core/core.h"
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
Manager::Manager(Core::System& system_) Manager::Manager(Core::System& system_)
: system{system_}, system_manager{std::make_unique<SystemManager>(system)} { : system_manager{std::make_unique<SystemManager>(system_)}
{
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
} }
@@ -38,14 +43,12 @@ Result Manager::GetWorkBufferSize(const AudioRendererParameterInternal& params,
s32 Manager::GetSessionId() { s32 Manager::GetSessionId() {
std::scoped_lock l{session_lock}; std::scoped_lock l{session_lock};
auto session_id{session_ids[session_count]}; ASSERT(session_count <= session_ids.size());
auto const session_id = session_ids[session_count];
if (session_id == -1) { if (session_id >= 0) {
return -1; session_ids[session_count] = -1;
session_count++;
} }
session_ids[session_count] = -1;
session_count++;
return session_id; return session_id;
} }
@@ -59,11 +62,11 @@ u32 Manager::GetSessionCount() const {
return session_count; return session_count;
} }
bool Manager::AddSystem(System& system_) { bool Manager::AddSystem(Renderer::System& system_) {
return system_manager->Add(system_); return system_manager->Add(system_);
} }
bool Manager::RemoveSystem(System& system_) { bool Manager::RemoveSystem(Renderer::System& system_) {
return system_manager->Remove(system_); return system_manager->Remove(system_);
} }
+5 -4
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -71,7 +74,7 @@ public:
* @param system - The system to add. * @param system - The system to add.
* @return True if the system was successfully added, otherwise false. * @return True if the system was successfully added, otherwise false.
*/ */
bool AddSystem(System& system); bool AddSystem(Renderer::System& system);
/** /**
* Remove a renderer system from the manager. * Remove a renderer system from the manager.
@@ -79,7 +82,7 @@ public:
* @param system - The system to remove. * @param system - The system to remove.
* @return True if the system was successfully removed, otherwise false. * @return True if the system was successfully removed, otherwise false.
*/ */
bool RemoveSystem(System& system); bool RemoveSystem(Renderer::System& system);
/** /**
* Free a session id when the system wants to shut down. * Free a session id when the system wants to shut down.
@@ -89,8 +92,6 @@ public:
void ReleaseSessionId(s32 session_id); void ReleaseSessionId(s32 session_id);
private: private:
/// Core system
Core::System& system;
/// Session ids, -1 when in use /// Session ids, -1 when in use
std::array<s32, MaxRendererSessions> session_ids{}; std::array<s32, MaxRendererSessions> session_ids{};
/// Number of active renderers /// Number of active renderers
+24 -20
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,42 +11,43 @@
namespace AudioCore::AudioIn { namespace AudioCore::AudioIn {
In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_) In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event, : manager{manager_}, parent_mutex{manager.mutex}, event{event_}
session_id_} {} , audio_system{system_, event, session_id_}
{}
void In::Free() { void In::Free(Core::System& system) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
manager.ReleaseSessionId(system.GetSessionId()); manager.ReleaseSessionId(system, audio_system.GetSessionId());
} }
System& In::GetSystem() { System& In::GetSystem() {
return system; return audio_system;
} }
AudioIn::State In::GetState() { AudioIn::State In::GetState() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetState(); return audio_system.GetState();
} }
Result In::StartSystem() { Result In::StartSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.Start(); return audio_system.Start();
} }
void In::StartSession() { void In::StartSession() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
system.StartSession(); audio_system.StartSession();
} }
Result In::StopSystem() { Result In::StopSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.Stop(); return audio_system.Stop();
} }
Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) { Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (system.AppendBuffer(buffer, tag)) { if (audio_system.AppendBuffer(buffer, tag)) {
return ResultSuccess; return ResultSuccess;
} }
return Service::Audio::ResultBufferCountReached; return Service::Audio::ResultBufferCountReached;
@@ -51,20 +55,20 @@ Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
void In::ReleaseAndRegisterBuffers() { void In::ReleaseAndRegisterBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (system.GetState() == State::Started) { if (audio_system.GetState() == State::Started) {
system.ReleaseBuffers(); audio_system.ReleaseBuffers();
system.RegisterBuffers(); audio_system.RegisterBuffers();
} }
} }
bool In::FlushAudioInBuffers() { bool In::FlushAudioInBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.FlushAudioInBuffers(); return audio_system.FlushAudioInBuffers();
} }
u32 In::GetReleasedBuffers(std::span<u64> tags) { u32 In::GetReleasedBuffers(std::span<u64> tags) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetReleasedBuffers(tags); return audio_system.GetReleasedBuffers(tags);
} }
Kernel::KReadableEvent& In::GetBufferEvent() { Kernel::KReadableEvent& In::GetBufferEvent() {
@@ -74,27 +78,27 @@ Kernel::KReadableEvent& In::GetBufferEvent() {
f32 In::GetVolume() const { f32 In::GetVolume() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetVolume(); return audio_system.GetVolume();
} }
void In::SetVolume(f32 volume) { void In::SetVolume(f32 volume) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
system.SetVolume(volume); audio_system.SetVolume(volume);
} }
bool In::ContainsAudioBuffer(u64 tag) const { bool In::ContainsAudioBuffer(u64 tag) const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.ContainsAudioBuffer(tag); return audio_system.ContainsAudioBuffer(tag);
} }
u32 In::GetBufferCount() const { u32 In::GetBufferCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetBufferCount(); return audio_system.GetBufferCount();
} }
u64 In::GetPlayedSampleCount() const { u64 In::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetPlayedSampleCount(); return audio_system.GetPlayedSampleCount();
} }
} // namespace AudioCore::AudioIn } // namespace AudioCore::AudioIn
+5 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -30,7 +33,7 @@ public:
/** /**
* Free this audio in from the audio in manager. * Free this audio in from the audio in manager.
*/ */
void Free(); void Free(Core::System& system);
/** /**
* Get this audio in's system. * Get this audio in's system.
@@ -141,7 +144,7 @@ private:
/// Buffer event, signalled when buffers are ready to be released /// Buffer event, signalled when buffers are ready to be released
Kernel::KEvent* event; Kernel::KEvent* event;
/// Main audio in system /// Main audio in system
System system; System audio_system;
}; };
} // namespace AudioCore::AudioIn } // namespace AudioCore::AudioIn
+24 -20
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,42 +11,43 @@
namespace AudioCore::AudioOut { namespace AudioCore::AudioOut {
Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_) Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event, : manager{manager_}, parent_mutex{manager.mutex}, event{event_}
session_id_} {} , audio_system{system_, event, session_id_}
{}
void Out::Free() { void Out::Free(Core::System& system) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
manager.ReleaseSessionId(system.GetSessionId()); manager.ReleaseSessionId(system, audio_system.GetSessionId());
} }
System& Out::GetSystem() { System& Out::GetSystem() {
return system; return audio_system;
} }
AudioOut::State Out::GetState() { AudioOut::State Out::GetState() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetState(); return audio_system.GetState();
} }
Result Out::StartSystem() { Result Out::StartSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.Start(); return audio_system.Start();
} }
void Out::StartSession() { void Out::StartSession() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
system.StartSession(); audio_system.StartSession();
} }
Result Out::StopSystem() { Result Out::StopSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.Stop(); return audio_system.Stop();
} }
Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) { Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (system.AppendBuffer(buffer, tag)) { if (audio_system.AppendBuffer(buffer, tag)) {
return ResultSuccess; return ResultSuccess;
} }
return Service::Audio::ResultBufferCountReached; return Service::Audio::ResultBufferCountReached;
@@ -51,20 +55,20 @@ Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
void Out::ReleaseAndRegisterBuffers() { void Out::ReleaseAndRegisterBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (system.GetState() == State::Started) { if (audio_system.GetState() == State::Started) {
system.ReleaseBuffers(); audio_system.ReleaseBuffers();
system.RegisterBuffers(); audio_system.RegisterBuffers();
} }
} }
bool Out::FlushAudioOutBuffers() { bool Out::FlushAudioOutBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.FlushAudioOutBuffers(); return audio_system.FlushAudioOutBuffers();
} }
u32 Out::GetReleasedBuffers(std::span<u64> tags) { u32 Out::GetReleasedBuffers(std::span<u64> tags) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetReleasedBuffers(tags); return audio_system.GetReleasedBuffers(tags);
} }
Kernel::KReadableEvent& Out::GetBufferEvent() { Kernel::KReadableEvent& Out::GetBufferEvent() {
@@ -74,27 +78,27 @@ Kernel::KReadableEvent& Out::GetBufferEvent() {
f32 Out::GetVolume() const { f32 Out::GetVolume() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetVolume(); return audio_system.GetVolume();
} }
void Out::SetVolume(const f32 volume) { void Out::SetVolume(const f32 volume) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
system.SetVolume(volume); audio_system.SetVolume(volume);
} }
bool Out::ContainsAudioBuffer(const u64 tag) const { bool Out::ContainsAudioBuffer(const u64 tag) const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.ContainsAudioBuffer(tag); return audio_system.ContainsAudioBuffer(tag);
} }
u32 Out::GetBufferCount() const { u32 Out::GetBufferCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetBufferCount(); return audio_system.GetBufferCount();
} }
u64 Out::GetPlayedSampleCount() const { u64 Out::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return system.GetPlayedSampleCount(); return audio_system.GetPlayedSampleCount();
} }
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+5 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -30,7 +33,7 @@ public:
/** /**
* Free this audio out from the audio out manager. * Free this audio out from the audio out manager.
*/ */
void Free(); void Free(Core::System& system);
/** /**
* Get this audio out's system. * Get this audio out's system.
@@ -141,7 +144,7 @@ private:
/// Buffer event, signalled when buffers are ready to be released /// Buffer event, signalled when buffers are ready to be released
Kernel::KEvent* event; Kernel::KEvent* event;
/// Main audio out system /// Main audio out system
System system; System audio_system;
}; };
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+18 -23
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,56 +16,48 @@
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event) Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event)
: core{system_}, manager{manager_}, system{system_, rendered_event} {} : system{system_}, manager{manager_}
, audio_system{system_, rendered_event}
{}
Result Renderer::Initialize(const AudioRendererParameterInternal& params, Result Renderer::Initialize(const AudioRendererParameterInternal& params, Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size, Kernel::KProcess* process_handle, const u64 applet_resource_user_id, const s32 session_id) {
Kernel::KTransferMemory* transfer_memory,
const u64 transfer_memory_size, Kernel::KProcess* process_handle,
const u64 applet_resource_user_id, const s32 session_id) {
if (params.execution_mode == ExecutionMode::Auto) { if (params.execution_mode == ExecutionMode::Auto) {
if (!manager.AddSystem(system)) { if (!manager.AddSystem(audio_system)) {
LOG_ERROR(Service_Audio, LOG_ERROR(Service_Audio, "Both Audio Render sessions are in use, cannot create any more");
"Both Audio Render sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
} }
system_registered = true; system_registered = true;
} }
initialized = true; initialized = true;
system.Initialize(params, transfer_memory, transfer_memory_size, process_handle, audio_system.Initialize(params, transfer_memory, transfer_memory_size, process_handle, applet_resource_user_id, session_id);
applet_resource_user_id, session_id);
return ResultSuccess; return ResultSuccess;
} }
void Renderer::Finalize() { void Renderer::Finalize() {
auto session_id{system.GetSessionId()}; auto const session_id{audio_system.GetSessionId()};
audio_system.Finalize();
system.Finalize();
if (system_registered) { if (system_registered) {
manager.RemoveSystem(system); manager.RemoveSystem(audio_system);
system_registered = false; system_registered = false;
} }
manager.ReleaseSessionId(session_id); manager.ReleaseSessionId(session_id);
} }
System& Renderer::GetSystem() { System& Renderer::GetSystem() {
return system; return audio_system;
} }
void Renderer::Start() { void Renderer::Start() {
system.Start(); audio_system.Start();
} }
void Renderer::Stop() { void Renderer::Stop() {
system.Stop(); audio_system.Stop();
} }
Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance, Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) {
std::span<u8> output) { return audio_system.Update(input, performance, output);
return system.Update(input, performance, output);
} }
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+5 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -84,7 +87,7 @@ public:
private: private:
/// System core /// System core
Core::System& core; Core::System& system;
/// Manager this renderer is registered with /// Manager this renderer is registered with
Manager& manager; Manager& manager;
/// Is the audio renderer initialized? /// Is the audio renderer initialized?
@@ -92,7 +95,7 @@ private:
/// Is the system registered with the manager? /// Is the system registered with the manager?
bool system_registered{}; bool system_registered{};
/// Audio render system, main driver of audio rendering /// Audio render system, main driver of audio rendering
System system; System audio_system;
}; };
} // namespace Renderer } // namespace Renderer
+8
View File
@@ -28,10 +28,18 @@ namespace {
// //
// Keep in sync with cubeb_sink.cpp name. // Keep in sync with cubeb_sink.cpp name.
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter"); SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter");
#ifdef __ANDROID__
SDL_SetHintWithPriority(SDL_HINT_AUDIO_DRIVER, "openslES", SDL_HINT_OVERRIDE);
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) { if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
LOG_WARNING(Audio_Sink, "OpenSL ES audio initialization failed: {}; retrying default drivers", SDL_GetError());
SDL_ResetHint(SDL_HINT_AUDIO_DRIVER);
}
#endif
if (!SDL_WasInit(SDL_INIT_AUDIO) && !SDL_InitSubSystem(SDL_INIT_AUDIO)) {
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError()); LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
return false; return false;
} }
LOG_INFO(Audio_Sink, "SDL audio driver: {}", SDL_GetCurrentAudioDriver());
} }
return true; return true;
} }
+18 -6
View File
@@ -1261,11 +1261,23 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITE
target_link_libraries(core PRIVATE dynarmic::dynarmic) target_link_libraries(core PRIVATE dynarmic::dynarmic)
endif() endif()
if (TARGET OpenSSL::SSL) target_sources(core PRIVATE hle/service/ssl/ssl_backend_openssl.cpp)
target_sources(core PRIVATE hle/service/ssl/ssl_backend_openssl.cpp)
target_link_libraries(core PRIVATE OpenSSL::SSL OpenSSL::Crypto) target_link_libraries(core PRIVATE OpenSSL::SSL OpenSSL::Crypto)
else()
target_sources(core PRIVATE hle/service/ssl/ssl_backend_none.cpp) # TODO
endif()
# elseif (APPLE)
# target_sources(core PRIVATE
# hle/service/ssl/ssl_backend_securetransport.cpp)
# target_link_libraries(core PRIVATE "-framework Security")
# elseif (WIN32)
# target_sources(core PRIVATE
# hle/service/ssl/ssl_backend_schannel.cpp)
# target_link_libraries(core PRIVATE crypt32 secur32)
# else()
# target_sources(core PRIVATE
# hle/service/ssl/ssl_backend_none.cpp)
# endif()
create_target_directory_groups(core) create_target_directory_groups(core)
+17 -17
View File
@@ -185,26 +185,26 @@ public:
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("aud:a", std::make_shared<IAudioSystemManagerForApplet>(system)); server_manager->RegisterNamedService("aud:a", std::make_shared<IAudioSystemManagerForApplet>(system), 30);
server_manager->RegisterNamedService("aud:d", std::make_shared<IAudioSystemManagerForDebugger>(system)); server_manager->RegisterNamedService("aud:d", std::make_shared<IAudioSystemManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system)); server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system)); server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system)); server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system)); server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system), 30);
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system)); server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system)); server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
server_manager->RegisterNamedService("audout:u", std::make_shared<IAudioOutManager>(system)); server_manager->RegisterNamedService("audout:u", std::make_shared<IAudioOutManager>(system), 30);
server_manager->RegisterNamedService("audout:a", std::make_shared<IAudioOutManagerForApplet>(system)); server_manager->RegisterNamedService("audout:a", std::make_shared<IAudioOutManagerForApplet>(system), 30);
server_manager->RegisterNamedService("auddev", std::make_shared<IAudioSnoopManager>(system)); server_manager->RegisterNamedService("auddev", std::make_shared<IAudioSnoopManager>(system), 30);
// Depends on audout:u and audin:u on ctor! // Depends on audout:u and audin:u on ctor!
server_manager->RegisterNamedService("audctl", std::make_shared<IAudioController>(system)); server_manager->RegisterNamedService("audctl", std::make_shared<IAudioController>(system), 30);
server_manager->RegisterNamedService("audrec:a", std::make_shared<IFinalOutputRecorderManagerForApplet>(system)); server_manager->RegisterNamedService("audrec:a", std::make_shared<IFinalOutputRecorderManagerForApplet>(system), 30);
server_manager->RegisterNamedService("audrec:u", std::make_shared<IFinalOutputRecorderManager>(system)); server_manager->RegisterNamedService("audrec:u", std::make_shared<IFinalOutputRecorderManager>(system), 30);
server_manager->RegisterNamedService("audren:u", std::make_shared<IAudioRendererManager>(system)); server_manager->RegisterNamedService("audren:u", std::make_shared<IAudioRendererManager>(system), 30);
server_manager->RegisterNamedService("audren:a", std::make_shared<IAudioRendererManagerForApplet>(system)); server_manager->RegisterNamedService("audren:a", std::make_shared<IAudioRendererManagerForApplet>(system), 30);
server_manager->RegisterNamedService("hwopus", std::make_shared<IHardwareOpusDecoderManager>(system)); server_manager->RegisterNamedService("hwopus", std::make_shared<IHardwareOpusDecoderManager>(system), 25);
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+1 -1
View File
@@ -50,7 +50,7 @@ IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
} }
IAudioIn::~IAudioIn() { IAudioIn::~IAudioIn() {
impl->Free(); impl->Free(system);
service_context.CloseEvent(event); service_context.CloseEvent(event);
process->Close(system.Kernel()); process->Close(system.Kernel());
} }
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -65,7 +68,7 @@ Result IAudioInManager::OpenAudioInAuto(
Result IAudioInManager::ListAudioInsAutoFiltered( Result IAudioInManager::ListAudioInsAutoFiltered(
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) { OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) {
LOG_DEBUG(Service_Audio, "called"); LOG_DEBUG(Service_Audio, "called");
*out_count = impl->GetDeviceNames(out_audio_ins, true); *out_count = impl->GetDeviceNames(system, out_audio_ins, true);
R_SUCCEED(); R_SUCCEED();
} }
@@ -90,8 +93,8 @@ Result IAudioInManager::OpenAudioInProtocolSpecified(
size_t new_session_id{}; size_t new_session_id{};
R_TRY(impl->LinkToManager()); R_TRY(impl->LinkToManager(system));
R_TRY(impl->AcquireSessionId(new_session_id)); R_TRY(impl->AcquireSessionId(system, new_session_id));
LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id, LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id,
impl->num_free_sessions); impl->num_free_sessions);
+1 -1
View File
@@ -46,7 +46,7 @@ IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id,
} }
IAudioOut::~IAudioOut() { IAudioOut::~IAudioOut() {
impl->Free(); impl->Free(system);
service_context.CloseEvent(event); service_context.CloseEvent(event);
process->Close(system.Kernel()); process->Close(system.Kernel());
} }
@@ -77,8 +77,8 @@ Result IAudioOutManager::OpenAudioOutAuto(
} }
size_t new_session_id{}; size_t new_session_id{};
R_TRY(impl->LinkToManager()); R_TRY(impl->LinkToManager(system));
R_TRY(impl->AcquireSessionId(new_session_id)); R_TRY(impl->AcquireSessionId(system, new_session_id));
const auto device_name = Common::StringFromBuffer(name[0].name); const auto device_name = Common::StringFromBuffer(name[0].name);
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
+12 -18
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -12,25 +15,16 @@ namespace Service::BCAT {
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("bcat:a", server_manager->RegisterNamedService("bcat:a", std::make_shared<IServiceCreator>(system, "bcat:a"), 32);
std::make_shared<IServiceCreator>(system, "bcat:a")); server_manager->RegisterNamedService("bcat:m", std::make_shared<IServiceCreator>(system, "bcat:m"), 32);
server_manager->RegisterNamedService("bcat:m", server_manager->RegisterNamedService("bcat:u", std::make_shared<IServiceCreator>(system, "bcat:u"), 32);
std::make_shared<IServiceCreator>(system, "bcat:m")); server_manager->RegisterNamedService("bcat:s", std::make_shared<IServiceCreator>(system, "bcat:s"), 32);
server_manager->RegisterNamedService("bcat:u",
std::make_shared<IServiceCreator>(system, "bcat:u"));
server_manager->RegisterNamedService("bcat:s",
std::make_shared<IServiceCreator>(system, "bcat:s"));
server_manager->RegisterNamedService( server_manager->RegisterNamedService("news:a", std::make_shared<News::IServiceCreator>(system, 0xffffffff, "news:a"), 32);
"news:a", std::make_shared<News::IServiceCreator>(system, 0xffffffff, "news:a")); server_manager->RegisterNamedService("news:p", std::make_shared<News::IServiceCreator>(system, 0x1, "news:p"), 32);
server_manager->RegisterNamedService( server_manager->RegisterNamedService("news:c", std::make_shared<News::IServiceCreator>(system, 0x2, "news:c"), 32);
"news:p", std::make_shared<News::IServiceCreator>(system, 0x1, "news:p")); server_manager->RegisterNamedService("news:v", std::make_shared<News::IServiceCreator>(system, 0x4, "news:v"), 32);
server_manager->RegisterNamedService( server_manager->RegisterNamedService("news:m", std::make_shared<News::IServiceCreator>(system, 0xd, "news:m"), 32);
"news:c", std::make_shared<News::IServiceCreator>(system, 0x2, "news:c"));
server_manager->RegisterNamedService(
"news:v", std::make_shared<News::IServiceCreator>(system, 0x4, "news:v"));
server_manager->RegisterNamedService(
"news:m", std::make_shared<News::IServiceCreator>(system, 0xd, "news:m"));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+19 -5
View File
@@ -101,14 +101,28 @@ public:
} }
}; };
class BPC_AMS final : public ServiceFramework<BPC_AMS> {
public:
explicit BPC_AMS(Core::System& system_) : ServiceFramework{system_, "bpc:ams"} {
// clang-format off
static const FunctionInfo functions[] = {
{65000, nullptr, "RebootToFatalError"},
{65001, nullptr, "SetRebootPayload"},
};
// clang-format on
RegisterHandlers(functions);
}
};
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("bpc", std::make_shared<BPC>(system)); server_manager->RegisterNamedService("bpc", std::make_shared<BPC>(system), 13);
server_manager->RegisterNamedService("bpc:r", std::make_shared<BPC_R>(system)); server_manager->RegisterNamedService("bpc:r", std::make_shared<BPC_R>(system), 13);
server_manager->RegisterNamedService("bpc:c", std::make_shared<BPC_C>(system)); server_manager->RegisterNamedService("bpc:c", std::make_shared<BPC_C>(system), 13);
server_manager->RegisterNamedService("bpc:b", std::make_shared<BPC_B>(system)); server_manager->RegisterNamedService("bpc:b", std::make_shared<BPC_B>(system), 13);
server_manager->RegisterNamedService("bpc:w", std::make_shared<BPC_W>(system)); server_manager->RegisterNamedService("bpc:w", std::make_shared<BPC_W>(system), 13);
server_manager->RegisterNamedService("bpc:ams", std::make_shared<BPC_AMS>(system), 4);
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
@@ -804,9 +804,9 @@ void LoopProcess(Core::System& system) {
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); }; const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system)); server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system), 61);
server_manager->RegisterNamedService("fsp-pr", std::make_shared<FSP_PR>(system)); server_manager->RegisterNamedService("fsp-pr", std::make_shared<FSP_PR>(system), 61);
server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory)); server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory), 61);
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+15 -20
View File
@@ -36,18 +36,18 @@ std::optional<u64> GetTitleIDForProcessID(Core::System& system, u64 process_id)
ARP_R::ARP_R(Core::System& system_, const ARPManager& manager_) ARP_R::ARP_R(Core::System& system_, const ARPManager& manager_)
: ServiceFramework{system_, "arp:r"}, manager{manager_} { : ServiceFramework{system_, "arp:r"}, manager{manager_} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &ARP_R::GetApplicationLaunchProperty, "GetApplicationLaunchProperty"}, {0, &ARP_R::GetApplicationLaunchProperty, "GetApplicationLaunchProperty"},
{1, &ARP_R::GetApplicationLaunchPropertyWithApplicationId, "GetApplicationLaunchPropertyWithApplicationId"}, {1, &ARP_R::GetApplicationLaunchPropertyWithApplicationId, "GetApplicationLaunchPropertyWithApplicationId"},
{2, &ARP_R::GetApplicationControlProperty, "GetApplicationControlProperty"}, {2, &ARP_R::GetApplicationControlProperty, "GetApplicationControlProperty"},
{3, &ARP_R::GetApplicationControlPropertyWithApplicationId, "GetApplicationControlPropertyWithApplicationId"}, {3, &ARP_R::GetApplicationControlPropertyWithApplicationId, "GetApplicationControlPropertyWithApplicationId"},
{4, nullptr, "GetApplicationInstanceUnregistrationNotifier"}, {4, nullptr, "GetApplicationInstanceUnregistrationNotifier"},
{5, nullptr, "ListApplicationInstanceId"}, {5, nullptr, "ListApplicationInstanceId"},
{6, nullptr, "GetMicroApplicationInstanceId"}, {6, nullptr, "GetMicroApplicationInstanceId"},
{7, nullptr, "GetApplicationCertificate"}, {7, nullptr, "GetApplicationCertificate"},
{9998, nullptr, "GetPreomiaApplicationLaunchProperty"}, {9998, nullptr, "GetPreomiaApplicationLaunchProperty"},
{9999, nullptr, "GetPreomiaApplicationControlProperty"}, {9999, nullptr, "GetPreomiaApplicationControlProperty"},
}; };
// clang-format on // clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
@@ -191,8 +191,7 @@ private:
} }
if (issued) { if (issued) {
LOG_ERROR(Service_ARP, LOG_ERROR(Service_ARP, "Attempted to issue registrar, but registrar is already issued!");
"Attempted to issue registrar, but registrar is already issued!");
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(Glue::ResultAlreadyBound); rb.Push(Glue::ResultAlreadyBound);
return; return;
@@ -209,9 +208,7 @@ private:
LOG_DEBUG(Service_ARP, "called"); LOG_DEBUG(Service_ARP, "called");
if (issued) { if (issued) {
LOG_ERROR( LOG_ERROR(Service_ARP, "Attempted to set application launch property, but registrar is already issued!");
Service_ARP,
"Attempted to set application launch property, but registrar is already issued!");
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(Glue::ResultAlreadyBound); rb.Push(Glue::ResultAlreadyBound);
return; return;
@@ -228,9 +225,7 @@ private:
LOG_DEBUG(Service_ARP, "called"); LOG_DEBUG(Service_ARP, "called");
if (issued) { if (issued) {
LOG_ERROR( LOG_ERROR(Service_ARP, "Attempted to set application control property, but registrar is already issued!");
Service_ARP,
"Attempted to set application control property, but registrar is already issued!");
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(Glue::ResultAlreadyBound); rb.Push(Glue::ResultAlreadyBound);
return; return;
+2 -2
View File
@@ -22,8 +22,8 @@ void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
// ARP // ARP
server_manager->RegisterNamedService("arp:r", std::make_shared<ARP_R>(system, system.GetARPManager())); server_manager->RegisterNamedService("arp:r", std::make_shared<ARP_R>(system, system.GetARPManager()), 16);
server_manager->RegisterNamedService("arp:w", std::make_shared<ARP_W>(system, system.GetARPManager())); server_manager->RegisterNamedService("arp:w", std::make_shared<ARP_W>(system, system.GetARPManager()), 8);
// BackGround Task Controller // BackGround Task Controller
server_manager->RegisterNamedService("bgtc:t", std::make_shared<BGTC_T>(system)); server_manager->RegisterNamedService("bgtc:t", std::make_shared<BGTC_T>(system));
+2 -2
View File
@@ -46,8 +46,8 @@ public:
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("grc:c", std::make_shared<GRC>(system)); server_manager->RegisterNamedService("grc:c", std::make_shared<GRC>(system), 4);
server_manager->RegisterNamedService("grc:d", std::make_shared<GRC_D>(system)); server_manager->RegisterNamedService("grc:d", std::make_shared<GRC_D>(system), 4);
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+6 -3
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -56,9 +59,9 @@ public:
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("ldr:dmnt", std::make_shared<DebugMonitor>(system)); server_manager->RegisterNamedService("ldr:dmnt", std::make_shared<DebugMonitor>(system), 3);
server_manager->RegisterNamedService("ldr:pm", std::make_shared<ProcessManager>(system)); server_manager->RegisterNamedService("ldr:pm", std::make_shared<ProcessManager>(system), 1);
server_manager->RegisterNamedService("ldr:shel", std::make_shared<Shell>(system)); server_manager->RegisterNamedService("ldr:shel", std::make_shared<Shell>(system), 3);
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+3 -3
View File
@@ -169,9 +169,9 @@ public:
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system)); server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4);
server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system)); server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4);
server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system)); server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system), 4);
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+3 -6
View File
@@ -1144,12 +1144,9 @@ private:
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("nifm:a", server_manager->RegisterNamedService("nifm:a", std::make_shared<NetworkInterface>("nifm:a", system), 2);
std::make_shared<NetworkInterface>("nifm:a", system)); server_manager->RegisterNamedService("nifm:s", std::make_shared<NetworkInterface>("nifm:s", system), 16);
server_manager->RegisterNamedService("nifm:s", server_manager->RegisterNamedService("nifm:u", std::make_shared<NetworkInterface>("nifm:u", system), 5);
std::make_shared<NetworkInterface>("nifm:s", system));
server_manager->RegisterNamedService("nifm:u",
std::make_shared<NetworkInterface>("nifm:u", system));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+9 -9
View File
@@ -81,16 +81,16 @@ public:
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("ns:am2", std::make_shared<IServiceGetterInterface>(system, "ns:am2")); server_manager->RegisterNamedService("ns:am2", std::make_shared<IServiceGetterInterface>(system, "ns:am2"), 5);
server_manager->RegisterNamedService("ns:ec", std::make_shared<IServiceGetterInterface>(system, "ns:ec")); server_manager->RegisterNamedService("ns:ec", std::make_shared<IServiceGetterInterface>(system, "ns:ec"), 5);
server_manager->RegisterNamedService("ns:rid", std::make_shared<IServiceGetterInterface>(system, "ns:rid")); server_manager->RegisterNamedService("ns:rid", std::make_shared<IServiceGetterInterface>(system, "ns:rid"), 5);
server_manager->RegisterNamedService("ns:rt", std::make_shared<IServiceGetterInterface>(system, "ns:rt")); server_manager->RegisterNamedService("ns:rt", std::make_shared<IServiceGetterInterface>(system, "ns:rt"), 5);
server_manager->RegisterNamedService("ns:web", std::make_shared<IServiceGetterInterface>(system, "ns:web")); server_manager->RegisterNamedService("ns:web", std::make_shared<IServiceGetterInterface>(system, "ns:web"), 5);
server_manager->RegisterNamedService("ns:ro", std::make_shared<IServiceGetterInterface>(system, "ns:ro")); server_manager->RegisterNamedService("ns:ro", std::make_shared<IServiceGetterInterface>(system, "ns:ro"), 5);
server_manager->RegisterNamedService("ns:dev", std::make_shared<IDevelopInterface>(system)); server_manager->RegisterNamedService("ns:dev", std::make_shared<IDevelopInterface>(system), 5);
server_manager->RegisterNamedService("ns:su", std::make_shared<ISystemUpdateInterface>(system)); server_manager->RegisterNamedService("ns:su", std::make_shared<ISystemUpdateInterface>(system), 5);
server_manager->RegisterNamedService("ns:vm", std::make_shared<IVulnerabilityManagerInterface>(system)); server_manager->RegisterNamedService("ns:vm", std::make_shared<IVulnerabilityManagerInterface>(system), 5);
server_manager->RegisterNamedService("pdm:ntfy", std::make_shared<INotifyService>(system)); server_manager->RegisterNamedService("pdm:ntfy", std::make_shared<INotifyService>(system));
server_manager->RegisterNamedService("pdm:qry", std::make_shared<IQueryService>(system)); server_manager->RegisterNamedService("pdm:qry", std::make_shared<IQueryService>(system));
+4 -4
View File
@@ -252,10 +252,10 @@ private:
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("pm:bm", std::make_shared<BootMode>(system)); server_manager->RegisterNamedService("pm:bm", std::make_shared<BootMode>(system), 4); // Nx = 4, Ams = 8
server_manager->RegisterNamedService("pm:dmnt", std::make_shared<DebugMonitor>(system)); server_manager->RegisterNamedService("pm:dmnt", std::make_shared<DebugMonitor>(system), 16);
server_manager->RegisterNamedService("pm:info", std::make_shared<Info>(system)); server_manager->RegisterNamedService("pm:shell", std::make_shared<Shell>(system), 3); //Nx = 3, AMS = 8
server_manager->RegisterNamedService("pm:shell", std::make_shared<Shell>(system)); server_manager->RegisterNamedService("pm:info", std::make_shared<Info>(system), 25); //48-(4+16+3)
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+3 -3
View File
@@ -593,9 +593,9 @@ void LoopProcess(Core::System& system) {
return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User); return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User);
}; };
server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser)); server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser), 2);
server_manager->RegisterNamedService("ro:1", std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin)); server_manager->RegisterNamedService("ro:1", std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin), 2);
server_manager->RegisterNamedService("ro:dmnt", std::make_shared<IDebugMonitorInterface>(system)); server_manager->RegisterNamedService("ro:dmnt", std::make_shared<IDebugMonitorInterface>(system), 2);
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+7 -7
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,13 +16,10 @@ namespace Service::Set {
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("set", std::make_shared<ISettingsServer>(system)); server_manager->RegisterNamedService("set", std::make_shared<ISettingsServer>(system), 60);
server_manager->RegisterNamedService("set:cal", server_manager->RegisterNamedService("set:cal", std::make_shared<IFactorySettingsServer>(system), 60);
std::make_shared<IFactorySettingsServer>(system)); server_manager->RegisterNamedService("set:fd", std::make_shared<IFirmwareDebugSettingsServer>(system), 60);
server_manager->RegisterNamedService("set:fd", server_manager->RegisterNamedService("set:sys", std::make_shared<ISystemSettingsServer>(system), 60);
std::make_shared<IFirmwareDebugSettingsServer>(system));
server_manager->RegisterNamedService("set:sys",
std::make_shared<ISystemSettingsServer>(system));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+3 -4
View File
@@ -53,8 +53,7 @@ static Result ValidateServiceName(const std::string& name) {
return ResultSuccess; return ResultSuccess;
} }
Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, std::string name, Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, std::string name, u32 max_sessions, SessionRequestHandlerFactory handler) {
u32 max_sessions, SessionRequestHandlerFactory handler) {
R_TRY(ValidateServiceName(name)); R_TRY(ValidateServiceName(name));
std::scoped_lock lk{lock}; std::scoped_lock lk{lock};
@@ -64,7 +63,7 @@ Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, st
} }
auto* port = Kernel::KPort::Create(kernel); auto* port = Kernel::KPort::Create(kernel);
port->Initialize(kernel, ServerSessionCountMax, false, 0); port->Initialize(kernel, max_sessions, false, 0);
// Register the port. // Register the port.
Kernel::KPort::Register(kernel, port); Kernel::KPort::Register(kernel, port);
@@ -264,7 +263,7 @@ void SM::AtmosphereHasService(HLERequestContext& ctx) {
} }
SM::SM(ServiceManager& service_manager_, Core::System& system_) SM::SM(ServiceManager& service_manager_, Core::System& system_)
: ServiceFramework{system_, "sm:", 4} : ServiceFramework{system_, "sm:", 64}
, service_manager{service_manager_} , service_manager{service_manager_}
, kernel{system_.Kernel()} , kernel{system_.Kernel()}
{ {
+7 -7
View File
@@ -64,17 +64,17 @@ public:
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("ethc:c", std::make_shared<ETHC_C>(system)); server_manager->RegisterNamedService("ethc:c", std::make_shared<ETHC_C>(system), 5);
server_manager->RegisterNamedService("ethc:i", std::make_shared<ETHC_I>(system)); server_manager->RegisterNamedService("ethc:i", std::make_shared<ETHC_I>(system), 5);
server_manager->RegisterNamedService("bsd:s", std::make_shared<BSD_USA>(system, "bsd:s", false)); server_manager->RegisterNamedService("bsd:s", std::make_shared<BSD_USA>(system, "bsd:s", false), 0x7E);
server_manager->RegisterNamedService("bsd:u", std::make_shared<BSD_USA>(system, "bsd:u", true)); server_manager->RegisterNamedService("bsd:u", std::make_shared<BSD_USA>(system, "bsd:u", true), 0x0f);
server_manager->RegisterNamedService("bsd:a", std::make_shared<BSD_USA>(system, "bsd:a", true)); server_manager->RegisterNamedService("bsd:a", std::make_shared<BSD_USA>(system, "bsd:a", true), 0x17);
server_manager->RegisterNamedService("bsd:nu", std::make_shared<BSD_NU>(system)); server_manager->RegisterNamedService("bsd:nu", std::make_shared<BSD_NU>(system), 4);
server_manager->RegisterNamedService("bsdcfg", std::make_shared<BSDCFG>(system, "bsdcfg")); server_manager->RegisterNamedService("bsdcfg", std::make_shared<BSDCFG>(system, "bsdcfg"));
server_manager->RegisterNamedService("ifcfg", std::make_shared<BSDCFG>(system, "ifcfg")); server_manager->RegisterNamedService("ifcfg", std::make_shared<BSDCFG>(system, "ifcfg"));
server_manager->RegisterNamedService("nsd:a", std::make_shared<NSD>(system, "nsd:a")); server_manager->RegisterNamedService("nsd:a", std::make_shared<NSD>(system, "nsd:a"));
server_manager->RegisterNamedService("nsd:u", std::make_shared<NSD>(system, "nsd:u")); server_manager->RegisterNamedService("nsd:u", std::make_shared<NSD>(system, "nsd:u"));
server_manager->RegisterNamedService("sfdnsres", std::make_shared<SFDNSRES>(system)); server_manager->RegisterNamedService("sfdnsres", std::make_shared<SFDNSRES>(system), 30);
server_manager->RegisterNamedService("dns:priv", std::make_shared<DNS_PRIV>(system)); server_manager->RegisterNamedService("dns:priv", std::make_shared<DNS_PRIV>(system));
server_manager->RegisterNamedService("eth:nd", std::make_shared<ISfDriverServiceCreator>(system)); server_manager->RegisterNamedService("eth:nd", std::make_shared<ISfDriverServiceCreator>(system));
@@ -0,0 +1,563 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include "common/error.h"
#include "common/fs/file.h"
#include "common/hex_util.h"
#include "common/string_util.h"
#include "core/hle/service/ssl/ssl_backend.h"
#include "core/internal_network/network.h"
#include "core/internal_network/sockets.h"
namespace {
// These includes are inside the namespace to avoid a conflict on MinGW where
// the headers define an enum containing Network and Service as enumerators
// (which clash with the correspondingly named namespaces).
#define SECURITY_WIN32
#include <schnlsp.h>
#include <security.h>
#include <wincrypt.h>
std::once_flag one_time_init_flag;
bool one_time_init_success = false;
SCHANNEL_CRED schannel_cred{};
CredHandle cred_handle;
static void OneTimeInit() {
schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
schannel_cred.dwFlags =
SCH_USE_STRONG_CRYPTO | // don't allow insecure protocols
SCH_CRED_NO_SERVERNAME_CHECK | // don't validate server names
SCH_CRED_NO_DEFAULT_CREDS; // don't automatically present a client certificate
// ^ I'm assuming that nobody would want to connect Yuzu to a
// service that requires some OS-provided corporate client
// certificate, and presenting one to some arbitrary server
// might be a privacy concern? Who knows, though.
const SECURITY_STATUS ret =
AcquireCredentialsHandle(nullptr, const_cast<LPTSTR>(UNISP_NAME), SECPKG_CRED_OUTBOUND,
nullptr, &schannel_cred, nullptr, nullptr, &cred_handle, nullptr);
if (ret != SEC_E_OK) {
// SECURITY_STATUS codes are a type of HRESULT and can be used with NativeErrorToString.
LOG_ERROR(Service_SSL, "AcquireCredentialsHandle failed: {}",
Common::NativeErrorToString(ret));
return;
}
if (getenv("SSLKEYLOGFILE")) {
LOG_CRITICAL(Service_SSL, "SSLKEYLOGFILE was set but Schannel does not support exporting "
"keys; not logging keys!");
// Not fatal.
}
one_time_init_success = true;
}
} // namespace
namespace Service::SSL {
class SSLConnectionBackendSchannel final : public SSLConnectionBackend {
public:
Result Init() {
std::call_once(one_time_init_flag, OneTimeInit);
if (!one_time_init_success) {
LOG_ERROR(
Service_SSL,
"Can't create SSL connection because Schannel one-time initialization failed");
return ResultInternalError;
}
return ResultSuccess;
}
void SetSocket(std::shared_ptr<Network::SocketBase> socket_in) override {
socket = std::move(socket_in);
}
Result SetHostName(const std::string& hostname_in) override {
hostname = hostname_in;
return ResultSuccess;
}
void SetVerifyOption(u32 option) override {
skip_cert_verification = (option == 0);
LOG_WARNING(Service_SSL, "option={} skip_verification={}", option,
skip_cert_verification);
}
Result DoHandshake() override {
while (1) {
Result r;
switch (handshake_state) {
case HandshakeState::Initial:
if ((r = FlushCiphertextWriteBuf()) != ResultSuccess ||
(r = CallInitializeSecurityContext()) != ResultSuccess) {
return r;
}
// CallInitializeSecurityContext updated `handshake_state`.
continue;
case HandshakeState::ContinueNeeded:
case HandshakeState::IncompleteMessage:
if ((r = FlushCiphertextWriteBuf()) != ResultSuccess ||
(r = FillCiphertextReadBuf()) != ResultSuccess) {
return r;
}
if (ciphertext_read_buf.empty()) {
LOG_ERROR(Service_SSL, "SSL handshake failed because server hung up");
return ResultInternalError;
}
if ((r = CallInitializeSecurityContext()) != ResultSuccess) {
return r;
}
// CallInitializeSecurityContext updated `handshake_state`.
continue;
case HandshakeState::DoneAfterFlush:
if ((r = FlushCiphertextWriteBuf()) != ResultSuccess) {
return r;
}
handshake_state = HandshakeState::Connected;
return ResultSuccess;
case HandshakeState::Connected:
LOG_ERROR(Service_SSL, "Called DoHandshake but we already handshook");
return ResultInternalError;
case HandshakeState::Error:
return ResultInternalError;
}
}
}
Result FillCiphertextReadBuf() {
const size_t fill_size = read_buf_fill_size ? read_buf_fill_size : 4096;
read_buf_fill_size = 0;
// This unnecessarily zeroes the buffer; oh well.
const size_t offset = ciphertext_read_buf.size();
ASSERT_OR_EXECUTE(offset + fill_size >= offset, { return ResultInternalError; });
ciphertext_read_buf.resize(offset + fill_size, 0);
const auto read_span = std::span(ciphertext_read_buf).subspan(offset, fill_size);
const auto [actual, err] = socket->Recv(0, read_span);
switch (err) {
case Network::Errno::SUCCESS:
ASSERT(static_cast<size_t>(actual) <= fill_size);
ciphertext_read_buf.resize(offset + actual);
return ResultSuccess;
case Network::Errno::AGAIN:
ciphertext_read_buf.resize(offset);
return ResultWouldBlock;
default:
ciphertext_read_buf.resize(offset);
LOG_ERROR(Service_SSL, "Socket recv returned Network::Errno {}", err);
return ResultInternalError;
}
}
// Returns success if the write buffer has been completely emptied.
Result FlushCiphertextWriteBuf() {
while (!ciphertext_write_buf.empty()) {
const auto [actual, err] = socket->Send(ciphertext_write_buf, 0);
switch (err) {
case Network::Errno::SUCCESS:
ASSERT(static_cast<size_t>(actual) <= ciphertext_write_buf.size());
ciphertext_write_buf.erase(ciphertext_write_buf.begin(),
ciphertext_write_buf.begin() + actual);
break;
case Network::Errno::AGAIN:
return ResultWouldBlock;
default:
LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err);
return ResultInternalError;
}
}
return ResultSuccess;
}
Result CallInitializeSecurityContext() {
unsigned long req = ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY |
ISC_REQ_INTEGRITY | ISC_REQ_REPLAY_DETECT |
ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM |
ISC_REQ_USE_SUPPLIED_CREDS;
if (skip_cert_verification) {
req |= ISC_REQ_MANUAL_CRED_VALIDATION;
}
unsigned long attr;
// https://learn.microsoft.com/en-us/windows/win32/secauthn/initializesecuritycontext--schannel
std::array<SecBuffer, 2> input_buffers{{
// only used if `initial_call_done`
{
// [0]
.cbBuffer = static_cast<unsigned long>(ciphertext_read_buf.size()),
.BufferType = SECBUFFER_TOKEN,
.pvBuffer = ciphertext_read_buf.data(),
},
{
// [1] (will be replaced by SECBUFFER_MISSING when SEC_E_INCOMPLETE_MESSAGE is
// returned, or SECBUFFER_EXTRA when SEC_E_CONTINUE_NEEDED is returned if the
// whole buffer wasn't used)
.cbBuffer = 0,
.BufferType = SECBUFFER_EMPTY,
.pvBuffer = nullptr,
},
}};
std::array<SecBuffer, 2> output_buffers{{
{
.cbBuffer = 0,
.BufferType = SECBUFFER_TOKEN,
.pvBuffer = nullptr,
}, // [0]
{
.cbBuffer = 0,
.BufferType = SECBUFFER_ALERT,
.pvBuffer = nullptr,
}, // [1]
}};
SecBufferDesc input_desc{
.ulVersion = SECBUFFER_VERSION,
.cBuffers = static_cast<unsigned long>(input_buffers.size()),
.pBuffers = input_buffers.data(),
};
SecBufferDesc output_desc{
.ulVersion = SECBUFFER_VERSION,
.cBuffers = static_cast<unsigned long>(output_buffers.size()),
.pBuffers = output_buffers.data(),
};
ASSERT_OR_EXECUTE_MSG(
input_buffers[0].cbBuffer == ciphertext_read_buf.size(),
{ return ResultInternalError; }, "read buffer too large");
bool initial_call_done = handshake_state != HandshakeState::Initial;
if (initial_call_done) {
LOG_DEBUG(Service_SSL, "Passing {} bytes into InitializeSecurityContext",
ciphertext_read_buf.size());
}
char* hostname_ptr = hostname ? const_cast<char*>(hostname->c_str()) : nullptr;
const SECURITY_STATUS ret = InitializeSecurityContextA(
&cred_handle, initial_call_done ? &ctxt : nullptr, hostname_ptr, req,
0, // Reserved1
0, // TargetDataRep not used with Schannel
initial_call_done ? &input_desc : nullptr,
0, // Reserved2
initial_call_done ? nullptr : &ctxt, &output_desc, &attr,
nullptr); // ptsExpiry
if (output_buffers[0].pvBuffer) {
const std::span span(static_cast<u8*>(output_buffers[0].pvBuffer),
output_buffers[0].cbBuffer);
ciphertext_write_buf.insert(ciphertext_write_buf.end(), span.begin(), span.end());
FreeContextBuffer(output_buffers[0].pvBuffer);
}
if (output_buffers[1].pvBuffer) {
const std::span span(static_cast<u8*>(output_buffers[1].pvBuffer),
output_buffers[1].cbBuffer);
// The documentation doesn't explain what format this data is in.
LOG_DEBUG(Service_SSL, "Got a {}-byte alert buffer: {}", span.size(),
Common::HexToString(span));
}
switch (ret) {
case SEC_I_CONTINUE_NEEDED:
LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_I_CONTINUE_NEEDED");
if (input_buffers[1].BufferType == SECBUFFER_EXTRA) {
LOG_DEBUG(Service_SSL, "EXTRA of size {}", input_buffers[1].cbBuffer);
ASSERT(input_buffers[1].cbBuffer <= ciphertext_read_buf.size());
ciphertext_read_buf.erase(ciphertext_read_buf.begin(),
ciphertext_read_buf.end() - input_buffers[1].cbBuffer);
} else {
ASSERT(input_buffers[1].BufferType == SECBUFFER_EMPTY);
ciphertext_read_buf.clear();
}
handshake_state = HandshakeState::ContinueNeeded;
return ResultSuccess;
case SEC_E_INCOMPLETE_MESSAGE:
LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_E_INCOMPLETE_MESSAGE");
ASSERT(input_buffers[1].BufferType == SECBUFFER_MISSING);
read_buf_fill_size = input_buffers[1].cbBuffer;
handshake_state = HandshakeState::IncompleteMessage;
return ResultSuccess;
case SEC_E_OK:
LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_E_OK");
ciphertext_read_buf.clear();
handshake_state = HandshakeState::DoneAfterFlush;
return GrabStreamSizes();
default:
LOG_ERROR(Service_SSL,
"InitializeSecurityContext failed (probably certificate/protocol issue): {}",
Common::NativeErrorToString(ret));
handshake_state = HandshakeState::Error;
return ResultInternalError;
}
}
Result GrabStreamSizes() {
const SECURITY_STATUS ret =
QueryContextAttributes(&ctxt, SECPKG_ATTR_STREAM_SIZES, &stream_sizes);
if (ret != SEC_E_OK) {
LOG_ERROR(Service_SSL, "QueryContextAttributes(SECPKG_ATTR_STREAM_SIZES) failed: {}",
Common::NativeErrorToString(ret));
handshake_state = HandshakeState::Error;
return ResultInternalError;
}
return ResultSuccess;
}
Result Read(size_t* out_size, std::span<u8> data) override {
*out_size = 0;
if (handshake_state != HandshakeState::Connected) {
LOG_ERROR(Service_SSL, "Called Read but we did not successfully handshake");
return ResultInternalError;
}
if (data.size() == 0 || got_read_eof) {
return ResultSuccess;
}
while (1) {
if (!cleartext_read_buf.empty()) {
*out_size = (std::min)(cleartext_read_buf.size(), data.size());
std::memcpy(data.data(), cleartext_read_buf.data(), *out_size);
cleartext_read_buf.erase(cleartext_read_buf.begin(),
cleartext_read_buf.begin() + *out_size);
return ResultSuccess;
}
if (!ciphertext_read_buf.empty()) {
SecBuffer empty{
.cbBuffer = 0,
.BufferType = SECBUFFER_EMPTY,
.pvBuffer = nullptr,
};
std::array<SecBuffer, 5> buffers{{
{
.cbBuffer = static_cast<unsigned long>(ciphertext_read_buf.size()),
.BufferType = SECBUFFER_DATA,
.pvBuffer = ciphertext_read_buf.data(),
},
empty,
empty,
empty,
}};
ASSERT_OR_EXECUTE_MSG(
buffers[0].cbBuffer == ciphertext_read_buf.size(),
{ return ResultInternalError; }, "read buffer too large");
SecBufferDesc desc{
.ulVersion = SECBUFFER_VERSION,
.cBuffers = static_cast<unsigned long>(buffers.size()),
.pBuffers = buffers.data(),
};
SECURITY_STATUS ret =
DecryptMessage(&ctxt, &desc, /*MessageSeqNo*/ 0, /*pfQOP*/ nullptr);
switch (ret) {
case SEC_E_OK:
ASSERT_OR_EXECUTE(buffers[0].BufferType == SECBUFFER_STREAM_HEADER,
{ return ResultInternalError; });
ASSERT_OR_EXECUTE(buffers[1].BufferType == SECBUFFER_DATA,
{ return ResultInternalError; });
ASSERT_OR_EXECUTE(buffers[2].BufferType == SECBUFFER_STREAM_TRAILER,
{ return ResultInternalError; });
cleartext_read_buf.assign(static_cast<u8*>(buffers[1].pvBuffer),
static_cast<u8*>(buffers[1].pvBuffer) +
buffers[1].cbBuffer);
if (buffers[3].BufferType == SECBUFFER_EXTRA) {
ASSERT(buffers[3].cbBuffer <= ciphertext_read_buf.size());
ciphertext_read_buf.erase(ciphertext_read_buf.begin(),
ciphertext_read_buf.end() - buffers[3].cbBuffer);
} else {
ASSERT(buffers[3].BufferType == SECBUFFER_EMPTY);
ciphertext_read_buf.clear();
}
continue;
case SEC_E_INCOMPLETE_MESSAGE:
break;
case SEC_I_CONTEXT_EXPIRED:
// Server hung up by sending close_notify.
got_read_eof = true;
*out_size = 0;
return ResultSuccess;
default:
LOG_ERROR(Service_SSL, "DecryptMessage failed: {}",
Common::NativeErrorToString(ret));
return ResultInternalError;
}
}
const Result r = FillCiphertextReadBuf();
if (r != ResultSuccess) {
return r;
}
if (ciphertext_read_buf.empty()) {
got_read_eof = true;
*out_size = 0;
return ResultSuccess;
}
}
}
Result Write(size_t* out_size, std::span<const u8> data) override {
*out_size = 0;
if (handshake_state != HandshakeState::Connected) {
LOG_ERROR(Service_SSL, "Called Write but we did not successfully handshake");
return ResultInternalError;
}
if (data.size() == 0) {
return ResultSuccess;
}
data = data.subspan(0, std::min<size_t>(data.size(), stream_sizes.cbMaximumMessage));
if (!cleartext_write_buf.empty()) {
// Already in the middle of a write. It wouldn't make sense to not
// finish sending the entire buffer since TLS has
// header/MAC/padding/etc.
if (data.size() != cleartext_write_buf.size() ||
std::memcmp(data.data(), cleartext_write_buf.data(), data.size())) {
LOG_ERROR(Service_SSL, "Called Write but buffer does not match previous buffer");
return ResultInternalError;
}
return WriteAlreadyEncryptedData(out_size);
} else {
cleartext_write_buf.assign(data.begin(), data.end());
}
std::vector<u8> header_buf(stream_sizes.cbHeader, 0);
std::vector<u8> tmp_data_buf = cleartext_write_buf;
std::vector<u8> trailer_buf(stream_sizes.cbTrailer, 0);
std::array<SecBuffer, 3> buffers{{
{
.cbBuffer = stream_sizes.cbHeader,
.BufferType = SECBUFFER_STREAM_HEADER,
.pvBuffer = header_buf.data(),
},
{
.cbBuffer = static_cast<unsigned long>(tmp_data_buf.size()),
.BufferType = SECBUFFER_DATA,
.pvBuffer = tmp_data_buf.data(),
},
{
.cbBuffer = stream_sizes.cbTrailer,
.BufferType = SECBUFFER_STREAM_TRAILER,
.pvBuffer = trailer_buf.data(),
},
}};
ASSERT_OR_EXECUTE_MSG(
buffers[1].cbBuffer == tmp_data_buf.size(), { return ResultInternalError; },
"temp buffer too large");
SecBufferDesc desc{
.ulVersion = SECBUFFER_VERSION,
.cBuffers = static_cast<unsigned long>(buffers.size()),
.pBuffers = buffers.data(),
};
const SECURITY_STATUS ret = EncryptMessage(&ctxt, /*fQOP*/ 0, &desc, /*MessageSeqNo*/ 0);
if (ret != SEC_E_OK) {
LOG_ERROR(Service_SSL, "EncryptMessage failed: {}", Common::NativeErrorToString(ret));
return ResultInternalError;
}
ciphertext_write_buf.insert(ciphertext_write_buf.end(), header_buf.begin(),
header_buf.end());
ciphertext_write_buf.insert(ciphertext_write_buf.end(), tmp_data_buf.begin(),
tmp_data_buf.end());
ciphertext_write_buf.insert(ciphertext_write_buf.end(), trailer_buf.begin(),
trailer_buf.end());
return WriteAlreadyEncryptedData(out_size);
}
Result WriteAlreadyEncryptedData(size_t* out_size) {
const Result r = FlushCiphertextWriteBuf();
if (r != ResultSuccess) {
return r;
}
// write buf is empty
*out_size = cleartext_write_buf.size();
cleartext_write_buf.clear();
return ResultSuccess;
}
Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) override {
PCCERT_CONTEXT returned_cert = nullptr;
const SECURITY_STATUS ret =
QueryContextAttributes(&ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &returned_cert);
if (ret != SEC_E_OK) {
LOG_ERROR(Service_SSL,
"QueryContextAttributes(SECPKG_ATTR_REMOTE_CERT_CONTEXT) failed: {}",
Common::NativeErrorToString(ret));
return ResultInternalError;
}
PCCERT_CONTEXT some_cert = nullptr;
while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert)) !=
nullptr) {
out_certs->emplace_back(static_cast<u8*>(some_cert->pbCertEncoded),
static_cast<u8*>(some_cert->pbCertEncoded) +
some_cert->cbCertEncoded);
}
std::reverse(out_certs->begin(),
out_certs->end()); // Windows returns certs in reverse order from what we want
CertFreeCertificateContext(returned_cert);
return ResultSuccess;
}
~SSLConnectionBackendSchannel() {
if (handshake_state != HandshakeState::Initial) {
DeleteSecurityContext(&ctxt);
}
}
enum class HandshakeState {
// Haven't called anything yet.
Initial,
// `SEC_I_CONTINUE_NEEDED` was returned by
// `InitializeSecurityContext`; must finish sending data (if any) in
// the write buffer, then read at least one byte before calling
// `InitializeSecurityContext` again.
ContinueNeeded,
// `SEC_E_INCOMPLETE_MESSAGE` was returned by
// `InitializeSecurityContext`; hopefully the write buffer is empty;
// must read at least one byte before calling
// `InitializeSecurityContext` again.
IncompleteMessage,
// `SEC_E_OK` was returned by `InitializeSecurityContext`; must
// finish sending data in the write buffer before having `DoHandshake`
// report success.
DoneAfterFlush,
// We finished the above and are now connected. At this point, writing
// and reading are separate 'state machines' represented by the
// nonemptiness of the ciphertext and cleartext read and write buffers.
Connected,
// Another error was returned and we shouldn't allow initialization
// to continue.
Error,
} handshake_state = HandshakeState::Initial;
CtxtHandle ctxt;
SecPkgContext_StreamSizes stream_sizes;
std::shared_ptr<Network::SocketBase> socket;
std::optional<std::string> hostname;
std::vector<u8> ciphertext_read_buf;
std::vector<u8> ciphertext_write_buf;
std::vector<u8> cleartext_read_buf;
std::vector<u8> cleartext_write_buf;
bool got_read_eof = false;
bool skip_cert_verification = false;
size_t read_buf_fill_size = 0;
};
Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend) {
auto conn = std::make_unique<SSLConnectionBackendSchannel>();
R_TRY(conn->Init());
*out_backend = std::move(conn);
return ResultSuccess;
}
} // namespace Service::SSL
@@ -0,0 +1,236 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
// SecureTransport has been deprecated in its entirety in favor of
// Network.framework, but that does not allow layering TLS on top of an
// arbitrary socket.
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include <Security/SecureTransport.h>
#pragma GCC diagnostic pop
#endif
#include "core/hle/service/ssl/ssl_backend.h"
#include "core/internal_network/network.h"
#include "core/internal_network/sockets.h"
namespace {
template <typename T>
struct CFReleaser {
T ptr;
YUZU_NON_COPYABLE(CFReleaser);
constexpr CFReleaser() : ptr(nullptr) {}
constexpr CFReleaser(T ptr) : ptr(ptr) {}
constexpr operator T() {
return ptr;
}
~CFReleaser() {
if (ptr) {
CFRelease(ptr);
}
}
};
std::string CFStringToString(CFStringRef cfstr) {
CFReleaser<CFDataRef> cfdata(
CFStringCreateExternalRepresentation(nullptr, cfstr, kCFStringEncodingUTF8, 0));
ASSERT_OR_EXECUTE(cfdata, { return "???"; });
return std::string(reinterpret_cast<const char*>(CFDataGetBytePtr(cfdata)),
CFDataGetLength(cfdata));
}
std::string OSStatusToString(OSStatus status) {
CFReleaser<CFStringRef> cfstr(SecCopyErrorMessageString(status, nullptr));
if (!cfstr) {
return "[unknown error]";
}
return CFStringToString(cfstr);
}
} // namespace
namespace Service::SSL {
class SSLConnectionBackendSecureTransport final : public SSLConnectionBackend {
public:
Result Init() {
static std::once_flag once_flag;
std::call_once(once_flag, []() {
if (getenv("SSLKEYLOGFILE")) {
LOG_CRITICAL(Service_SSL, "SSLKEYLOGFILE was set but SecureTransport does not "
"support exporting keys; not logging keys!");
// Not fatal.
}
});
context.ptr = SSLCreateContext(nullptr, kSSLClientSide, kSSLStreamType);
if (!context) {
LOG_ERROR(Service_SSL, "SSLCreateContext failed");
return ResultInternalError;
}
OSStatus status;
if ((status = SSLSetIOFuncs(context, ReadCallback, WriteCallback)) ||
(status = SSLSetConnection(context, this))) {
LOG_ERROR(Service_SSL, "SSLContext initialization failed: {}",
OSStatusToString(status));
return ResultInternalError;
}
return ResultSuccess;
}
void SetSocket(std::shared_ptr<Network::SocketBase> in_socket) override {
socket = std::move(in_socket);
}
Result SetHostName(const std::string& hostname) override {
OSStatus status = SSLSetPeerDomainName(context, hostname.c_str(), hostname.size());
if (status) {
LOG_ERROR(Service_SSL, "SSLSetPeerDomainName failed: {}", OSStatusToString(status));
return ResultInternalError;
}
return ResultSuccess;
}
void SetVerifyOption(u32 option) override {
skip_cert_verification = (option == 0);
LOG_WARNING(Service_SSL, "option={} skip_verification={}", option,
skip_cert_verification);
if (skip_cert_verification) {
SSLSetSessionOption(context, kSSLSessionOptionBreakOnServerAuth, true);
}
}
Result DoHandshake() override {
OSStatus status = SSLHandshake(context);
if (skip_cert_verification && status == errSSLServerAuthCompleted) {
LOG_DEBUG(Service_SSL, "Skipping certificate verification as requested");
status = SSLHandshake(context);
}
return HandleReturn("SSLHandshake", 0, status);
}
Result Read(size_t* out_size, std::span<u8> data) override {
OSStatus status = SSLRead(context, data.data(), data.size(), out_size);
return HandleReturn("SSLRead", out_size, status);
}
Result Write(size_t* out_size, std::span<const u8> data) override {
OSStatus status = SSLWrite(context, data.data(), data.size(), out_size);
return HandleReturn("SSLWrite", out_size, status);
}
Result HandleReturn(const char* what, size_t* actual, OSStatus status) {
switch (status) {
case 0:
return ResultSuccess;
case errSSLWouldBlock:
return ResultWouldBlock;
default: {
std::string reason;
if (got_read_eof) {
reason = "server hung up";
} else {
reason = OSStatusToString(status);
}
LOG_ERROR(Service_SSL, "{} failed: {}", what, reason);
return ResultInternalError;
}
}
}
Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) override {
CFReleaser<SecTrustRef> trust;
OSStatus status = SSLCopyPeerTrust(context, &trust.ptr);
if (status) {
LOG_ERROR(Service_SSL, "SSLCopyPeerTrust failed: {}", OSStatusToString(status));
return ResultInternalError;
}
for (CFIndex i = 0, count = SecTrustGetCertificateCount(trust); i < count; i++) {
SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i);
CFReleaser<CFDataRef> data(SecCertificateCopyData(cert));
ASSERT_OR_EXECUTE(data, { return ResultInternalError; });
const u8* ptr = CFDataGetBytePtr(data);
out_certs->emplace_back(ptr, ptr + CFDataGetLength(data));
}
return ResultSuccess;
}
static OSStatus ReadCallback(SSLConnectionRef connection, void* data, size_t* dataLength) {
return ReadOrWriteCallback(connection, data, dataLength, true);
}
static OSStatus WriteCallback(SSLConnectionRef connection, const void* data,
size_t* dataLength) {
return ReadOrWriteCallback(connection, const_cast<void*>(data), dataLength, false);
}
static OSStatus ReadOrWriteCallback(SSLConnectionRef connection, void* data, size_t* dataLength,
bool is_read) {
auto self =
static_cast<SSLConnectionBackendSecureTransport*>(const_cast<void*>(connection));
ASSERT_OR_EXECUTE_MSG(
self->socket, { return 0; }, "SecureTransport asked to {} but we have no socket",
is_read ? "read" : "write");
// SecureTransport callbacks (unlike OpenSSL BIO callbacks) are
// expected to read/write the full requested dataLength or return an
// error, so we have to add a loop ourselves.
size_t requested_len = *dataLength;
size_t offset = 0;
while (offset < requested_len) {
std::span cur(reinterpret_cast<u8*>(data) + offset, requested_len - offset);
auto [actual, err] = is_read ? self->socket->Recv(0, cur) : self->socket->Send(cur, 0);
LOG_CRITICAL(Service_SSL, "op={}, offset={} actual={}/{} err={}", is_read, offset,
actual, cur.size(), static_cast<s32>(err));
switch (err) {
case Network::Errno::SUCCESS:
offset += actual;
if (actual == 0) {
ASSERT(is_read);
self->got_read_eof = true;
return errSecEndOfData;
}
break;
case Network::Errno::AGAIN:
*dataLength = offset;
return errSSLWouldBlock;
default:
LOG_ERROR(Service_SSL, "Socket {} returned Network::Errno {}",
is_read ? "recv" : "send", err);
return errSecIO;
}
}
ASSERT(offset == requested_len);
return 0;
}
private:
CFReleaser<SSLContextRef> context = nullptr;
bool got_read_eof = false;
bool skip_cert_verification = false;
std::shared_ptr<Network::SocketBase> socket;
};
Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend) {
auto conn = std::make_unique<SSLConnectionBackendSecureTransport>();
R_TRY(conn->Init());
*out_backend = std::move(conn);
return ResultSuccess;
}
} // namespace Service::SSL
+4 -4
View File
@@ -265,17 +265,17 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService("usb:ds", std::make_shared<IDsRootSession>(system)); server_manager->RegisterNamedService("usb:ds", std::make_shared<IDsRootSession>(system));
server_manager->RegisterNamedService("usb:hs", std::make_shared<IClientRootSession>(system)); server_manager->RegisterNamedService("usb:hs", std::make_shared<IClientRootSession>(system));
server_manager->RegisterNamedService("usb:pd", std::make_shared<IPdManager>(system)); server_manager->RegisterNamedService("usb:pd", std::make_shared<IPdManager>(system), 6);
server_manager->RegisterNamedService("usb:pd:c", std::make_shared<IPdCradleManager>(system)); server_manager->RegisterNamedService("usb:pd:c", std::make_shared<IPdCradleManager>(system), 4);
server_manager->RegisterNamedService("usb:pd:m", std::make_shared<IPdManufactureManager>(system)); server_manager->RegisterNamedService("usb:pd:m", std::make_shared<IPdManufactureManager>(system));
server_manager->RegisterNamedService("usb:pm", std::make_shared<IPmMainService>(system)); server_manager->RegisterNamedService("usb:pm", std::make_shared<IPmMainService>(system), 5);
// +7.0.0 // +7.0.0
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 7) { if (FirmwareManager::GetFirmwareVersion(system).first.major >= 7) {
server_manager->RegisterNamedService("usb:qdb", std::make_shared<IQdbManager>(system)); server_manager->RegisterNamedService("usb:qdb", std::make_shared<IQdbManager>(system));
} }
// +8.0.0 // +8.0.0
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 8) { if (FirmwareManager::GetFirmwareVersion(system).first.major >= 8) {
server_manager->RegisterNamedService("usb:obsv", std::make_shared<IPmObserverService>(system)); server_manager->RegisterNamedService("usb:obsv", std::make_shared<IPmObserverService>(system), 2);
} }
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+8 -8
View File
@@ -246,14 +246,14 @@ public:
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("wlan:lcl", std::make_shared<ILocalManager>(system)); server_manager->RegisterNamedService("wlan:lcl", std::make_shared<ILocalManager>(system), 10);
server_manager->RegisterNamedService("wlan:lg", std::make_shared<ILocalGetFrame>(system)); server_manager->RegisterNamedService("wlan:lg", std::make_shared<ILocalGetFrame>(system), 10);
server_manager->RegisterNamedService("wlan:lga", std::make_shared<ILocalGetActionFrame>(system)); server_manager->RegisterNamedService("wlan:lga", std::make_shared<ILocalGetActionFrame>(system), 10);
server_manager->RegisterNamedService("wlan:sg", std::make_shared<ISocketGetFrame>(system)); server_manager->RegisterNamedService("wlan:sg", std::make_shared<ISocketGetFrame>(system), 10);
server_manager->RegisterNamedService("wlan:soc", std::make_shared<ISocketManager>(system)); server_manager->RegisterNamedService("wlan:soc", std::make_shared<ISocketManager>(system), 10);
server_manager->RegisterNamedService("wlan:dtc", std::make_shared<IDetectManager>(system)); server_manager->RegisterNamedService("wlan:dtc", std::make_shared<IDetectManager>(system), 4);
server_manager->RegisterNamedService("wlan:p", std::make_shared<IPrivateServiceCreator>(system)); server_manager->RegisterNamedService("wlan:p", std::make_shared<IPrivateServiceCreator>(system), 30);
server_manager->RegisterNamedService("wlan:nd", std::make_shared<ISfDriverServiceCreator>(system)); server_manager->RegisterNamedService("wlan:nd", std::make_shared<ISfDriverServiceCreator>(system), 5);
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+5 -4
View File
@@ -55,8 +55,8 @@ constexpr u64 GpuClockMultiplier(Settings::GpuClock clock) {
struct GPU::Impl { struct GPU::Impl {
explicit Impl(Core::System& system_, bool is_async_, bool use_nvdec_) explicit Impl(Core::System& system_, bool is_async_, bool use_nvdec_)
: gpu_thread{system_} : system{system_}
, system{system_} , gpu_thread{system_}
, use_nvdec{use_nvdec_} , use_nvdec{use_nvdec_}
, shader_notify() , shader_notify()
, is_async{is_async_} , is_async{is_async_}
@@ -182,6 +182,7 @@ struct GPU::Impl {
} }
void NotifyShutdown() { void NotifyShutdown() {
gpu_thread.NotifyShutdown();
std::unique_lock lk{sync_mutex}; std::unique_lock lk{sync_mutex};
shutting_down.store(true, std::memory_order::relaxed); shutting_down.store(true, std::memory_order::relaxed);
sync_cv.notify_all(); sync_cv.notify_all();
@@ -301,12 +302,12 @@ struct GPU::Impl {
return out; return out;
} }
Core::System& system;
// Destruction of thread must be done before all (non trivial) // Destruction of thread must be done before all (non trivial)
// previous members has been destroyed // previous members has been destroyed
VideoCommon::GPUThread::ThreadManager gpu_thread; VideoCommon::GPUThread::ThreadManager gpu_thread;
Core::System& system;
std::unique_ptr<VideoCore::RendererBase> renderer; std::unique_ptr<VideoCore::RendererBase> renderer;
const bool use_nvdec; const bool use_nvdec;
+7
View File
@@ -114,4 +114,11 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block, bool is_a
return fence; return fence;
} }
void ThreadManager::NotifyShutdown() {
if (thread.joinable()) {
thread.request_stop();
thread.join();
}
}
} // namespace VideoCommon::GPUThread } // namespace VideoCommon::GPUThread
+2
View File
@@ -125,6 +125,8 @@ public:
void TickGPU(bool is_async); void TickGPU(bool is_async);
void NotifyShutdown();
private: private:
/// Pushes a command to be executed by the GPU thread /// Pushes a command to be executed by the GPU thread
u64 PushCommand(CommandData&& command_data, bool block, bool is_async); u64 PushCommand(CommandData&& command_data, bool block, bool is_async);