summaryrefslogtreecommitdiffstats
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/kernel/global_scheduler_context.cpp7
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp38
-rw-r--r--src/core/hle/kernel/hle_ipc.h8
-rw-r--r--src/core/hle/kernel/k_code_memory.h6
-rw-r--r--src/core/hle/kernel/k_interrupt_manager.cpp7
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp735
-rw-r--r--src/core/hle/kernel/k_scheduler.h223
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h2
-rw-r--r--src/core/hle/kernel/k_thread.cpp42
-rw-r--r--src/core/hle/kernel/k_thread.h26
-rw-r--r--src/core/hle/kernel/kernel.cpp61
-rw-r--r--src/core/hle/kernel/kernel.h3
-rw-r--r--src/core/hle/kernel/physical_core.cpp1
-rw-r--r--src/core/hle/kernel/svc.cpp7
-rw-r--r--src/core/hle/service/acc/acc.cpp20
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp11
-rw-r--r--src/core/hle/service/acc/profile_manager.h15
-rw-r--r--src/core/hle/service/am/am.cpp17
-rw-r--r--src/core/hle/service/am/am.h2
-rw-r--r--src/core/hle/service/audio/audin_u.cpp384
-rw-r--r--src/core/hle/service/audio/audin_u.h47
-rw-r--r--src/core/hle/service/audio/audout_u.cpp327
-rw-r--r--src/core/hle/service/audio/audout_u.h21
-rw-r--r--src/core/hle/service/audio/audren_u.cpp692
-rw-r--r--src/core/hle/service/audio/audren_u.h22
-rw-r--r--src/core/hle/service/audio/errors.h9
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/bcat/bcat_module.cpp4
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp5
-rw-r--r--src/core/hle/service/btm/btm.cpp8
-rw-r--r--src/core/hle/service/fatal/fatal_p.cpp8
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp4
-rw-r--r--src/core/hle/service/hid/errors.h7
-rw-r--r--src/core/hle/service/hid/hid.cpp4
-rw-r--r--src/core/hle/service/hid/irs.cpp324
-rw-r--r--src/core/hle/service/hid/irs.h295
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.cpp34
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.h74
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.cpp150
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.h73
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.cpp27
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.h47
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.cpp34
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.h61
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.cpp26
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.h61
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.cpp67
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.h33
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp29
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.h53
-rw-r--r--src/core/hle/service/nifm/nifm.cpp42
-rw-r--r--src/core/hle/service/nvflinger/binder.h1
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp18
-rw-r--r--src/core/hle/service/ptm/psm.cpp122
-rw-r--r--src/core/hle/service/ptm/psm.h31
-rw-r--r--src/core/hle/service/ptm/ptm.cpp18
-rw-r--r--src/core/hle/service/ptm/ptm.h18
-rw-r--r--src/core/hle/service/ptm/ts.cpp41
-rw-r--r--src/core/hle/service/ptm/ts.h25
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/sockets/bsd.cpp20
61 files changed, 2780 insertions, 1723 deletions
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp
index 164436b26..65576b8c4 100644
--- a/src/core/hle/kernel/global_scheduler_context.cpp
+++ b/src/core/hle/kernel/global_scheduler_context.cpp
@@ -41,12 +41,7 @@ void GlobalSchedulerContext::PreemptThreads() {
ASSERT(IsLocked());
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
const u32 priority = preemption_priorities[core_id];
- kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
-
- // Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
- // in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
- // interrupts that may have occurred.
- kernel.PhysicalCore(core_id).Interrupt();
+ KScheduler::RotateScheduledQueue(kernel, core_id, priority);
}
}
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 45135a07f..5b3feec66 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -287,18 +287,52 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size() >= size,
{ return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
- memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
+ WriteBufferB(buffer, size, buffer_index);
} else {
ASSERT_OR_EXECUTE_MSG(
BufferDescriptorC().size() > buffer_index &&
BufferDescriptorC()[buffer_index].Size() >= size,
{ return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
- memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
+ WriteBufferC(buffer, size, buffer_index);
}
return size;
}
+std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size,
+ std::size_t buffer_index) const {
+ if (buffer_index >= BufferDescriptorB().size() || size == 0) {
+ return 0;
+ }
+
+ const auto buffer_size{BufferDescriptorB()[buffer_index].Size()};
+ if (size > buffer_size) {
+ LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
+ buffer_size);
+ size = buffer_size; // TODO(bunnei): This needs to be HW tested
+ }
+
+ memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
+ return size;
+}
+
+std::size_t HLERequestContext::WriteBufferC(const void* buffer, std::size_t size,
+ std::size_t buffer_index) const {
+ if (buffer_index >= BufferDescriptorC().size() || size == 0) {
+ return 0;
+ }
+
+ const auto buffer_size{BufferDescriptorC()[buffer_index].Size()};
+ if (size > buffer_size) {
+ LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
+ buffer_size);
+ size = buffer_size; // TODO(bunnei): This needs to be HW tested
+ }
+
+ memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
+ return size;
+}
+
std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const {
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index d3abeee85..99265ce90 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -277,6 +277,14 @@ public:
std::size_t WriteBuffer(const void* buffer, std::size_t size,
std::size_t buffer_index = 0) const;
+ /// Helper function to write buffer B
+ std::size_t WriteBufferB(const void* buffer, std::size_t size,
+ std::size_t buffer_index = 0) const;
+
+ /// Helper function to write buffer C
+ std::size_t WriteBufferC(const void* buffer, std::size_t size,
+ std::size_t buffer_index = 0) const;
+
/* Helper function to write a buffer using the appropriate buffer descriptor
*
* @tparam T an arbitrary container that satisfies the
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
index 2410f74a3..2e7e1436a 100644
--- a/src/core/hle/kernel/k_code_memory.h
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -30,19 +30,19 @@ public:
explicit KCodeMemory(KernelCore& kernel_);
Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
- void Finalize();
+ void Finalize() override;
Result Map(VAddr address, size_t size);
Result Unmap(VAddr address, size_t size);
Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
Result UnmapFromOwner(VAddr address, size_t size);
- bool IsInitialized() const {
+ bool IsInitialized() const override {
return m_is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
- KProcess* GetOwner() const {
+ KProcess* GetOwner() const override {
return m_owner;
}
VAddr GetSourceAddress() const {
diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp
index d606a7f86..1b577a5b3 100644
--- a/src/core/hle/kernel/k_interrupt_manager.cpp
+++ b/src/core/hle/kernel/k_interrupt_manager.cpp
@@ -6,6 +6,7 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
namespace Kernel::KInterruptManager {
@@ -15,6 +16,9 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
return;
}
+ // Acknowledge the interrupt.
+ kernel.PhysicalCore(core_id).ClearInterrupt();
+
auto& current_thread = GetCurrentThread(kernel);
// If the user disable count is set, we may need to pin the current thread.
@@ -27,6 +31,9 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
// Set the interrupt flag for the thread.
GetCurrentThread(kernel).SetInterruptFlag();
}
+
+ // Request interrupt scheduling.
+ kernel.CurrentScheduler()->RequestScheduleOnInterrupt();
}
} // namespace Kernel::KInterruptManager
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index d599d2bcb..c34ce7a17 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -27,69 +27,185 @@ static void IncrementScheduledCount(Kernel::KThread* thread) {
}
}
-void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) {
- auto scheduler = kernel.CurrentScheduler();
-
- u32 current_core{0xF};
- bool must_context_switch{};
- if (scheduler) {
- current_core = scheduler->core_id;
- // TODO(bunnei): Should be set to true when we deprecate single core
- must_context_switch = !kernel.IsPhantomModeForSingleCore();
- }
-
- while (cores_pending_reschedule != 0) {
- const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
- ASSERT(core < Core::Hardware::NUM_CPU_CORES);
- if (!must_context_switch || core != current_core) {
- auto& phys_core = kernel.PhysicalCore(core);
- phys_core.Interrupt();
+KScheduler::KScheduler(KernelCore& kernel_) : kernel{kernel_} {
+ m_switch_fiber = std::make_shared<Common::Fiber>([this] {
+ while (true) {
+ ScheduleImplFiber();
}
- cores_pending_reschedule &= ~(1ULL << core);
+ });
+
+ m_state.needs_scheduling = true;
+}
+
+KScheduler::~KScheduler() = default;
+
+void KScheduler::SetInterruptTaskRunnable() {
+ m_state.interrupt_task_runnable = true;
+ m_state.needs_scheduling = true;
+}
+
+void KScheduler::RequestScheduleOnInterrupt() {
+ m_state.needs_scheduling = true;
+
+ if (CanSchedule(kernel)) {
+ ScheduleOnInterrupt();
}
+}
- for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
- if (kernel.PhysicalCore(core_id).IsInterrupted()) {
- KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
- }
+void KScheduler::DisableScheduling(KernelCore& kernel) {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
+ GetCurrentThread(kernel).DisableDispatch();
+}
+
+void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 1);
+
+ auto* scheduler{kernel.CurrentScheduler()};
+
+ if (!scheduler || kernel.IsPhantomModeForSingleCore()) {
+ KScheduler::RescheduleCores(kernel, cores_needing_scheduling);
+ KScheduler::RescheduleCurrentHLEThread(kernel);
+ return;
+ }
+
+ scheduler->RescheduleOtherCores(cores_needing_scheduling);
+
+ if (GetCurrentThread(kernel).GetDisableDispatchCount() > 1) {
+ GetCurrentThread(kernel).EnableDispatch();
+ } else {
+ scheduler->RescheduleCurrentCore();
+ }
+}
+
+void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) {
+ // HACK: we cannot schedule from this thread, it is not a core thread
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ // Special case to ensure dummy threads that are waiting block
+ GetCurrentThread(kernel).IfDummyThreadTryWait();
+
+ ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting);
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
+ if (IsSchedulerUpdateNeeded(kernel)) {
+ return UpdateHighestPriorityThreadsImpl(kernel);
+ } else {
+ return 0;
+ }
+}
+
+void KScheduler::Schedule() {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+ ASSERT(m_core_id == GetCurrentCoreId(kernel));
+
+ ScheduleImpl();
+}
+
+void KScheduler::ScheduleOnInterrupt() {
+ GetCurrentThread(kernel).DisableDispatch();
+ Schedule();
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+void KScheduler::PreemptSingleCore() {
+ GetCurrentThread(kernel).DisableDispatch();
+
+ auto* thread = GetCurrentThreadPointer(kernel);
+ auto& previous_scheduler = kernel.Scheduler(thread->GetCurrentCore());
+ previous_scheduler.Unload(thread);
+
+ Common::Fiber::YieldTo(thread->GetHostContext(), *m_switch_fiber);
+
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+void KScheduler::RescheduleCurrentCore() {
+ ASSERT(!kernel.IsPhantomModeForSingleCore());
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ GetCurrentThread(kernel).EnableDispatch();
+
+ if (m_state.needs_scheduling.load()) {
+ // Disable interrupts, and then check again if rescheduling is needed.
+ // KScopedInterruptDisable intr_disable;
+
+ kernel.CurrentScheduler()->RescheduleCurrentCoreImpl();
}
+}
- if (must_context_switch) {
- auto core_scheduler = kernel.CurrentScheduler();
- kernel.ExitSVCProfile();
- core_scheduler->RescheduleCurrentCore();
- kernel.EnterSVCProfile();
+void KScheduler::RescheduleCurrentCoreImpl() {
+ // Check that scheduling is needed.
+ if (m_state.needs_scheduling.load()) [[likely]] {
+ GetCurrentThread(kernel).DisableDispatch();
+ Schedule();
+ GetCurrentThread(kernel).EnableDispatch();
}
}
+void KScheduler::Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id) {
+ // Set core ID/idle thread/interrupt task manager.
+ m_core_id = core_id;
+ m_idle_thread = idle_thread;
+ // m_state.idle_thread_stack = m_idle_thread->GetStackTop();
+ // m_state.interrupt_task_manager = &kernel.GetInterruptTaskManager();
+
+ // Insert the main thread into the priority queue.
+ // {
+ // KScopedSchedulerLock lk{kernel};
+ // GetPriorityQueue(kernel).PushBack(GetCurrentThreadPointer(kernel));
+ // SetSchedulerUpdateNeeded(kernel);
+ // }
+
+ // Bind interrupt handler.
+ // kernel.GetInterruptManager().BindHandler(
+ // GetSchedulerInterruptHandler(kernel), KInterruptName::Scheduler, m_core_id,
+ // KInterruptController::PriorityLevel::Scheduler, false, false);
+
+ // Set the current thread.
+ m_current_thread = main_thread;
+}
+
+void KScheduler::Activate() {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ // m_state.should_count_idle = KTargetSystem::IsDebugMode();
+ m_is_active = true;
+ RescheduleCurrentCore();
+}
+
+void KScheduler::OnThreadStart() {
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
- KScopedSpinLock lk{guard};
- if (KThread* prev_highest_thread = state.highest_priority_thread;
- prev_highest_thread != highest_thread) {
- if (prev_highest_thread != nullptr) {
+ if (KThread* prev_highest_thread = m_state.highest_priority_thread;
+ prev_highest_thread != highest_thread) [[likely]] {
+ if (prev_highest_thread != nullptr) [[likely]] {
IncrementScheduledCount(prev_highest_thread);
- prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks());
+ prev_highest_thread->SetLastScheduledTick(kernel.System().CoreTiming().GetCPUTicks());
}
- if (state.should_count_idle) {
- if (highest_thread != nullptr) {
+ if (m_state.should_count_idle) {
+ if (highest_thread != nullptr) [[likely]] {
if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) {
- process->SetRunningThread(core_id, highest_thread, state.idle_count);
+ process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count);
}
} else {
- state.idle_count++;
+ m_state.idle_count++;
}
}
- state.highest_priority_thread = highest_thread;
- state.needs_scheduling.store(true);
- return (1ULL << core_id);
+ m_state.highest_priority_thread = highest_thread;
+ m_state.needs_scheduling = true;
+ return (1ULL << m_core_id);
} else {
return 0;
}
}
u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Clear that we need to update.
ClearSchedulerUpdateNeeded(kernel);
@@ -98,18 +214,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
KThread* top_threads[Core::Hardware::NUM_CPU_CORES];
auto& priority_queue = GetPriorityQueue(kernel);
- /// We want to go over all cores, finding the highest priority thread and determining if
- /// scheduling is needed for that core.
+ // We want to go over all cores, finding the highest priority thread and determining if
+ // scheduling is needed for that core.
for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
if (top_thread != nullptr) {
- // If the thread has no waiters, we need to check if the process has a thread pinned.
- if (top_thread->GetNumKernelWaiters() == 0) {
- if (KProcess* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
- if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
- pinned != nullptr && pinned != top_thread) {
- // We prefer our parent's pinned thread if possible. However, we also don't
- // want to schedule un-runnable threads.
+ // We need to check if the thread's process has a pinned thread.
+ if (KProcess* parent = top_thread->GetOwnerProcess()) {
+ // Check that there's a pinned thread other than the current top thread.
+ if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
+ pinned != nullptr && pinned != top_thread) {
+ // We need to prefer threads with kernel waiters to the pinned thread.
+ if (top_thread->GetNumKernelWaiters() ==
+ 0 /* && top_thread != parent->GetExceptionThread() */) {
+ // If the pinned thread is runnable, use it.
if (pinned->GetRawState() == ThreadState::Runnable) {
top_thread = pinned;
} else {
@@ -129,7 +247,8 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
while (idle_cores != 0) {
- const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
+ const s32 core_id = static_cast<s32>(std::countr_zero(idle_cores));
+
if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
size_t num_candidates = 0;
@@ -150,7 +269,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// The suggested thread isn't bound to its core, so we can migrate it!
suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested);
-
top_threads[core_id] = suggested;
cores_needing_scheduling |=
kernel.Scheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]);
@@ -183,7 +301,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Perform the migration.
suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(candidate_core, suggested);
-
top_threads[core_id] = suggested;
cores_needing_scheduling |=
kernel.Scheduler(core_id).UpdateHighestPriorityThread(
@@ -200,24 +317,210 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
return cores_needing_scheduling;
}
+void KScheduler::SwitchThread(KThread* next_thread) {
+ KProcess* const cur_process = kernel.CurrentProcess();
+ KThread* const cur_thread = GetCurrentThreadPointer(kernel);
+
+ // We never want to schedule a null thread, so use the idle thread if we don't have a next.
+ if (next_thread == nullptr) {
+ next_thread = m_idle_thread;
+ }
+
+ if (next_thread->GetCurrentCore() != m_core_id) {
+ next_thread->SetCurrentCore(m_core_id);
+ }
+
+ // If we're not actually switching thread, there's nothing to do.
+ if (next_thread == cur_thread) {
+ return;
+ }
+
+ // Next thread is now known not to be nullptr, and must not be dispatchable.
+ ASSERT(next_thread->GetDisableDispatchCount() == 1);
+ ASSERT(!next_thread->IsDummyThread());
+
+ // Update the CPU time tracking variables.
+ const s64 prev_tick = m_last_context_switch_time;
+ const s64 cur_tick = kernel.System().CoreTiming().GetCPUTicks();
+ const s64 tick_diff = cur_tick - prev_tick;
+ cur_thread->AddCpuTime(m_core_id, tick_diff);
+ if (cur_process != nullptr) {
+ cur_process->UpdateCPUTimeTicks(tick_diff);
+ }
+ m_last_context_switch_time = cur_tick;
+
+ // Update our previous thread.
+ if (cur_process != nullptr) {
+ if (!cur_thread->IsTerminationRequested() && cur_thread->GetActiveCore() == m_core_id)
+ [[likely]] {
+ m_state.prev_thread = cur_thread;
+ } else {
+ m_state.prev_thread = nullptr;
+ }
+ }
+
+ // Switch the current process, if we're switching processes.
+ // if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
+ // KProcess::Switch(cur_process, next_process);
+ // }
+
+ // Set the new thread.
+ SetCurrentThread(kernel, next_thread);
+ m_current_thread = next_thread;
+
+ // Set the new Thread Local region.
+ // cpu::SwitchThreadLocalRegion(GetInteger(next_thread->GetThreadLocalRegionAddress()));
+}
+
+void KScheduler::ScheduleImpl() {
+ // First, clear the needs scheduling bool.
+ m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
+
+ // Load the appropriate thread pointers for scheduling.
+ KThread* const cur_thread{GetCurrentThreadPointer(kernel)};
+ KThread* highest_priority_thread{m_state.highest_priority_thread};
+
+ // Check whether there are runnable interrupt tasks.
+ if (m_state.interrupt_task_runnable) {
+ // The interrupt task is runnable.
+ // We want to switch to the interrupt task/idle thread.
+ highest_priority_thread = nullptr;
+ }
+
+ // If there aren't, we want to check if the highest priority thread is the same as the current
+ // thread.
+ if (highest_priority_thread == cur_thread) {
+ // If they're the same, then we can just return.
+ return;
+ }
+
+ // The highest priority thread is not the same as the current thread.
+ // Jump to the switcher and continue executing from there.
+ m_switch_cur_thread = cur_thread;
+ m_switch_highest_priority_thread = highest_priority_thread;
+ m_switch_from_schedule = true;
+ Common::Fiber::YieldTo(cur_thread->host_context, *m_switch_fiber);
+
+ // Returning from ScheduleImpl occurs after this thread has been scheduled again.
+}
+
+void KScheduler::ScheduleImplFiber() {
+ KThread* const cur_thread{m_switch_cur_thread};
+ KThread* highest_priority_thread{m_switch_highest_priority_thread};
+
+ // If we're not coming from scheduling (i.e., we came from SC preemption),
+ // we should restart the scheduling loop directly. Not accurate to HOS.
+ if (!m_switch_from_schedule) {
+ goto retry;
+ }
+
+ // Mark that we are not coming from scheduling anymore.
+ m_switch_from_schedule = false;
+
+ // Save the original thread context.
+ Unload(cur_thread);
+
+ // The current thread's context has been entirely taken care of.
+ // Now we want to loop until we successfully switch the thread context.
+ while (true) {
+ // We're starting to try to do the context switch.
+ // Check if the highest priority thread is null.
+ if (!highest_priority_thread) {
+ // The next thread is nullptr!
+
+ // Switch to the idle thread. Note: HOS treats idling as a special case for
+ // performance. This is not *required* for yuzu's purposes, and for singlecore
+ // compatibility, we can just move the logic that would go here into the execution
+ // of the idle thread. If we ever remove singlecore, we should implement this
+ // accurately to HOS.
+ highest_priority_thread = m_idle_thread;
+ }
+
+ // We want to try to lock the highest priority thread's context.
+ // Try to take it.
+ while (!highest_priority_thread->context_guard.try_lock()) {
+ // The highest priority thread's context is already locked.
+ // Check if we need scheduling. If we don't, we can retry directly.
+ if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
+ // If we do, another core is interfering, and we must start again.
+ goto retry;
+ }
+ }
+
+ // It's time to switch the thread.
+ // Switch to the highest priority thread.
+ SwitchThread(highest_priority_thread);
+
+ // Check if we need scheduling. If we do, then we can't complete the switch and should
+ // retry.
+ if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
+ // Our switch failed.
+ // We should unlock the thread context, and then retry.
+ highest_priority_thread->context_guard.unlock();
+ goto retry;
+ } else {
+ break;
+ }
+
+ retry:
+
+ // We failed to successfully do the context switch, and need to retry.
+ // Clear needs_scheduling.
+ m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
+
+ // Refresh the highest priority thread.
+ highest_priority_thread = m_state.highest_priority_thread;
+ }
+
+ // Reload the guest thread context.
+ Reload(highest_priority_thread);
+
+ // Reload the host thread.
+ Common::Fiber::YieldTo(m_switch_fiber, *highest_priority_thread->host_context);
+}
+
+void KScheduler::Unload(KThread* thread) {
+ auto& cpu_core = kernel.System().ArmInterface(m_core_id);
+ cpu_core.SaveContext(thread->GetContext32());
+ cpu_core.SaveContext(thread->GetContext64());
+ // Save the TPIDR_EL0 system register in case it was modified.
+ thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
+ cpu_core.ClearExclusiveState();
+
+ // Check if the thread is terminated by checking the DPC flags.
+ if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
+ // The thread isn't terminated, so we want to unlock it.
+ thread->context_guard.unlock();
+ }
+}
+
+void KScheduler::Reload(KThread* thread) {
+ auto& cpu_core = kernel.System().ArmInterface(m_core_id);
+ cpu_core.LoadContext(thread->GetContext32());
+ cpu_core.LoadContext(thread->GetContext64());
+ cpu_core.SetTlsAddress(thread->GetTLSAddress());
+ cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
+ cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints());
+ cpu_core.ClearExclusiveState();
+}
+
void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) {
// Get an atomic reference to the core scheduler's previous thread.
- std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
- static_assert(std::atomic_ref<KThread*>::is_always_lock_free);
+ auto& prev_thread{kernel.Scheduler(i).m_state.prev_thread};
// Atomically clear the previous thread if it's our target.
KThread* compare = thread;
- prev_thread.compare_exchange_strong(compare, nullptr);
+ prev_thread.compare_exchange_strong(compare, nullptr, std::memory_order_seq_cst);
}
}
void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Check if the state has changed, because if it hasn't there's nothing to do.
- const auto cur_state = thread->GetRawState();
+ const ThreadState cur_state = thread->GetRawState();
if (cur_state == old_state) {
return;
}
@@ -237,12 +540,12 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa
}
void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// If the thread is runnable, we want to change its priority in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
GetPriorityQueue(kernel).ChangePriority(old_priority,
- thread == kernel.GetCurrentEmuThread(), thread);
+ thread == GetCurrentThreadPointer(kernel), thread);
IncrementScheduledCount(thread);
SetSchedulerUpdateNeeded(kernel);
}
@@ -250,7 +553,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3
void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// If the thread is runnable, we want to change its affinity in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
@@ -260,15 +563,14 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread
}
}
-void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
- ASSERT(system.GlobalSchedulerContext().IsLocked());
+void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority) {
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Get a reference to the priority queue.
- auto& kernel = system.Kernel();
auto& priority_queue = GetPriorityQueue(kernel);
// Rotate the front of the queue to the end.
- KThread* top_thread = priority_queue.GetScheduledFront(cpu_core_id, priority);
+ KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
KThread* next_thread = nullptr;
if (top_thread != nullptr) {
next_thread = priority_queue.MoveToScheduledBack(top_thread);
@@ -280,7 +582,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
// While we have a suggested thread, try to migrate it!
{
- KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id, priority);
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
@@ -301,7 +603,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
// to the front of the queue.
if (top_on_suggested_core == nullptr ||
top_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) {
- suggested->SetActiveCore(cpu_core_id);
+ suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested, true);
IncrementScheduledCount(suggested);
break;
@@ -309,22 +611,21 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
}
// Get the next suggestion.
- suggested = priority_queue.GetSamePriorityNext(cpu_core_id, suggested);
+ suggested = priority_queue.GetSamePriorityNext(core_id, suggested);
}
}
// Now that we might have migrated a thread with the same priority, check if we can do better.
-
{
- KThread* best_thread = priority_queue.GetScheduledFront(cpu_core_id);
+ KThread* best_thread = priority_queue.GetScheduledFront(core_id);
if (best_thread == GetCurrentThreadPointer(kernel)) {
- best_thread = priority_queue.GetScheduledNext(cpu_core_id, best_thread);
+ best_thread = priority_queue.GetScheduledNext(core_id, best_thread);
}
// If the best thread we can choose has a priority the same or worse than ours, try to
// migrate a higher priority thread.
if (best_thread != nullptr && best_thread->GetPriority() >= priority) {
- KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id);
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// If the suggestion's priority is the same as ours, don't bother.
if (suggested->GetPriority() >= best_thread->GetPriority()) {
@@ -343,7 +644,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
if (top_on_suggested_core == nullptr ||
top_on_suggested_core->GetPriority() >=
HighestCoreMigrationAllowedPriority) {
- suggested->SetActiveCore(cpu_core_id);
+ suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested, true);
IncrementScheduledCount(suggested);
break;
@@ -351,7 +652,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
}
// Get the next suggestion.
- suggested = priority_queue.GetSuggestedNext(cpu_core_id, suggested);
+ suggested = priority_queue.GetSuggestedNext(core_id, suggested);
}
}
}
@@ -360,64 +661,6 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
SetSchedulerUpdateNeeded(kernel);
}
-bool KScheduler::CanSchedule(KernelCore& kernel) {
- return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1;
-}
-
-bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) {
- return kernel.GlobalSchedulerContext().scheduler_update_needed.load(std::memory_order_acquire);
-}
-
-void KScheduler::SetSchedulerUpdateNeeded(KernelCore& kernel) {
- kernel.GlobalSchedulerContext().scheduler_update_needed.store(true, std::memory_order_release);
-}
-
-void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
- kernel.GlobalSchedulerContext().scheduler_update_needed.store(false, std::memory_order_release);
-}
-
-void KScheduler::DisableScheduling(KernelCore& kernel) {
- // If we are shutting down the kernel, none of this is relevant anymore.
- if (kernel.IsShuttingDown()) {
- return;
- }
-
- ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
- GetCurrentThreadPointer(kernel)->DisableDispatch();
-}
-
-void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
- // If we are shutting down the kernel, none of this is relevant anymore.
- if (kernel.IsShuttingDown()) {
- return;
- }
-
- auto* current_thread = GetCurrentThreadPointer(kernel);
-
- ASSERT(current_thread->GetDisableDispatchCount() >= 1);
-
- if (current_thread->GetDisableDispatchCount() > 1) {
- current_thread->EnableDispatch();
- } else {
- RescheduleCores(kernel, cores_needing_scheduling);
- }
-
- // Special case to ensure dummy threads that are waiting block.
- current_thread->IfDummyThreadTryWait();
-}
-
-u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
- if (IsSchedulerUpdateNeeded(kernel)) {
- return UpdateHighestPriorityThreadsImpl(kernel);
- } else {
- return 0;
- }
-}
-
-KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) {
- return kernel.GlobalSchedulerContext().priority_queue;
-}
-
void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
@@ -437,7 +680,7 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -476,7 +719,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -496,7 +739,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
if (KThread* running_on_suggested_core =
(suggested_core >= 0)
- ? kernel.Scheduler(suggested_core).state.highest_priority_thread
+ ? kernel.Scheduler(suggested_core).m_state.highest_priority_thread
: nullptr;
running_on_suggested_core != suggested) {
// If the current thread's priority is higher than our suggestion's we prefer
@@ -564,7 +807,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -621,223 +864,19 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
}
}
-KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} {
- switch_fiber = std::make_shared<Common::Fiber>([this] { SwitchToCurrent(); });
- state.needs_scheduling.store(true);
- state.interrupt_task_thread_runnable = false;
- state.should_count_idle = false;
- state.idle_count = 0;
- state.idle_thread_stack = nullptr;
- state.highest_priority_thread = nullptr;
-}
-
-void KScheduler::Finalize() {
- if (idle_thread) {
- idle_thread->Close();
- idle_thread = nullptr;
- }
-}
-
-KScheduler::~KScheduler() {
- ASSERT(!idle_thread);
-}
-
-KThread* KScheduler::GetSchedulerCurrentThread() const {
- if (auto result = current_thread.load(); result) {
- return result;
+void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) {
+ if (const u64 core_mask = cores_needing_scheduling & ~(1ULL << m_core_id); core_mask != 0) {
+ RescheduleCores(kernel, core_mask);
}
- return idle_thread;
-}
-
-u64 KScheduler::GetLastContextSwitchTicks() const {
- return last_context_switch_time;
}
-void KScheduler::RescheduleCurrentCore() {
- ASSERT(GetCurrentThread(system.Kernel()).GetDisableDispatchCount() == 1);
-
- auto& phys_core = system.Kernel().PhysicalCore(core_id);
- if (phys_core.IsInterrupted()) {
- phys_core.ClearInterrupt();
- }
-
- guard.Lock();
- if (state.needs_scheduling.load()) {
- Schedule();
- } else {
- GetCurrentThread(system.Kernel()).EnableDispatch();
- guard.Unlock();
- }
-}
-
-void KScheduler::OnThreadStart() {
- SwitchContextStep2();
-}
-
-void KScheduler::Unload(KThread* thread) {
- ASSERT(thread);
-
- LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
-
- if (thread->IsCallingSvc()) {
- thread->ClearIsCallingSvc();
- }
-
- auto& physical_core = system.Kernel().PhysicalCore(core_id);
- if (!physical_core.IsInitialized()) {
- return;
- }
-
- Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
- cpu_core.SaveContext(thread->GetContext32());
- cpu_core.SaveContext(thread->GetContext64());
- // Save the TPIDR_EL0 system register in case it was modified.
- thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
-
- if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
- prev_thread = thread;
- } else {
- prev_thread = nullptr;
- }
-
- thread->context_guard.unlock();
-}
-
-void KScheduler::Reload(KThread* thread) {
- LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread->GetName());
-
- Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
- cpu_core.LoadContext(thread->GetContext32());
- cpu_core.LoadContext(thread->GetContext64());
- cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints());
- cpu_core.SetTlsAddress(thread->GetTLSAddress());
- cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
-}
-
-void KScheduler::SwitchContextStep2() {
- // Load context of new thread
- Reload(GetCurrentThreadPointer(system.Kernel()));
-
- RescheduleCurrentCore();
-}
-
-void KScheduler::Schedule() {
- ASSERT(GetCurrentThread(system.Kernel()).GetDisableDispatchCount() == 1);
- this->ScheduleImpl();
-}
-
-void KScheduler::ScheduleImpl() {
- KThread* previous_thread = GetCurrentThreadPointer(system.Kernel());
- KThread* next_thread = state.highest_priority_thread;
-
- state.needs_scheduling.store(false);
-
- // We never want to schedule a null thread, so use the idle thread if we don't have a next.
- if (next_thread == nullptr) {
- next_thread = idle_thread;
- }
-
- if (next_thread->GetCurrentCore() != core_id) {
- next_thread->SetCurrentCore(core_id);
- }
-
- // We never want to schedule a dummy thread, as these are only used by host threads for locking.
- if (next_thread->GetThreadType() == ThreadType::Dummy) {
- ASSERT_MSG(false, "Dummy threads should never be scheduled!");
- next_thread = idle_thread;
- }
-
- // If we're not actually switching thread, there's nothing to do.
- if (next_thread == current_thread.load()) {
- previous_thread->EnableDispatch();
- guard.Unlock();
- return;
- }
-
- // Update the CPU time tracking variables.
- KProcess* const previous_process = system.Kernel().CurrentProcess();
- UpdateLastContextSwitchTime(previous_thread, previous_process);
-
- // Save context for previous thread
- Unload(previous_thread);
-
- std::shared_ptr<Common::Fiber>* old_context;
- old_context = &previous_thread->GetHostContext();
-
- // Set the new thread.
- SetCurrentThread(system.Kernel(), next_thread);
- current_thread.store(next_thread);
-
- guard.Unlock();
-
- Common::Fiber::YieldTo(*old_context, *switch_fiber);
- /// When a thread wakes up, the scheduler may have changed to other in another core.
- auto& next_scheduler = *system.Kernel().CurrentScheduler();
- next_scheduler.SwitchContextStep2();
-}
-
-void KScheduler::SwitchToCurrent() {
- while (true) {
- {
- KScopedSpinLock lk{guard};
- current_thread.store(state.highest_priority_thread);
- state.needs_scheduling.store(false);
+void KScheduler::RescheduleCores(KernelCore& kernel, u64 core_mask) {
+ // Send IPI
+ for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
+ if (core_mask & (1ULL << i)) {
+ kernel.PhysicalCore(i).Interrupt();
}
- const auto is_switch_pending = [this] {
- KScopedSpinLock lk{guard};
- return state.needs_scheduling.load();
- };
- do {
- auto next_thread = current_thread.load();
- if (next_thread != nullptr) {
- const auto locked = next_thread->context_guard.try_lock();
- if (state.needs_scheduling.load()) {
- next_thread->context_guard.unlock();
- break;
- }
- if (next_thread->GetActiveCore() != core_id) {
- next_thread->context_guard.unlock();
- break;
- }
- if (!locked) {
- continue;
- }
- }
- auto thread = next_thread ? next_thread : idle_thread;
- SetCurrentThread(system.Kernel(), thread);
- Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
- } while (!is_switch_pending());
}
}
-void KScheduler::UpdateLastContextSwitchTime(KThread* thread, KProcess* process) {
- const u64 prev_switch_ticks = last_context_switch_time;
- const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
- const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
-
- if (thread != nullptr) {
- thread->AddCpuTime(core_id, update_ticks);
- }
-
- if (process != nullptr) {
- process->UpdateCPUTimeTicks(update_ticks);
- }
-
- last_context_switch_time = most_recent_switch_ticks;
-}
-
-void KScheduler::Initialize() {
- idle_thread = KThread::Create(system.Kernel());
- ASSERT(KThread::InitializeIdleThread(system, idle_thread, core_id).IsSuccess());
- idle_thread->SetName(fmt::format("IdleThread:{}", core_id));
- idle_thread->EnableDispatch();
-}
-
-KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)
- : KScopedLock(kernel.GlobalSchedulerContext().SchedulerLock()) {}
-
-KScopedSchedulerLock::~KScopedSchedulerLock() = default;
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 6a4760eca..534321d8d 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -11,6 +11,7 @@
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/k_scoped_lock.h"
#include "core/hle/kernel/k_spin_lock.h"
+#include "core/hle/kernel/k_thread.h"
namespace Common {
class Fiber;
@@ -23,184 +24,150 @@ class System;
namespace Kernel {
class KernelCore;
+class KInterruptTaskManager;
class KProcess;
-class SchedulerLock;
class KThread;
+class KScopedDisableDispatch;
+class KScopedSchedulerLock;
+class KScopedSchedulerLockAndSleep;
class KScheduler final {
public:
- explicit KScheduler(Core::System& system_, s32 core_id_);
- ~KScheduler();
-
- void Finalize();
+ YUZU_NON_COPYABLE(KScheduler);
+ YUZU_NON_MOVEABLE(KScheduler);
- /// Reschedules to the next available thread (call after current thread is suspended)
- void RescheduleCurrentCore();
+ using LockType = KAbstractSchedulerLock<KScheduler>;
- /// Reschedules cores pending reschedule, to be called on EnableScheduling.
- static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule);
+ explicit KScheduler(KernelCore& kernel);
+ ~KScheduler();
- /// The next two are for SingleCore Only.
- /// Unload current thread before preempting core.
+ void Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id);
+ void Activate();
+ void OnThreadStart();
void Unload(KThread* thread);
-
- /// Reload current thread after core preemption.
void Reload(KThread* thread);
- /// Gets the current running thread
- [[nodiscard]] KThread* GetSchedulerCurrentThread() const;
+ void SetInterruptTaskRunnable();
+ void RequestScheduleOnInterrupt();
+ void PreemptSingleCore();
- /// Gets the idle thread
- [[nodiscard]] KThread* GetIdleThread() const {
- return idle_thread;
+ u64 GetIdleCount() {
+ return m_state.idle_count;
}
- /// Returns true if the scheduler is idle
- [[nodiscard]] bool IsIdle() const {
- return GetSchedulerCurrentThread() == idle_thread;
+ KThread* GetIdleThread() const {
+ return m_idle_thread;
}
- /// Gets the timestamp for the last context switch in ticks.
- [[nodiscard]] u64 GetLastContextSwitchTicks() const;
-
- [[nodiscard]] bool ContextSwitchPending() const {
- return state.needs_scheduling.load(std::memory_order_relaxed);
+ bool IsIdle() const {
+ return m_current_thread.load() == m_idle_thread;
}
- void Initialize();
+ KThread* GetPreviousThread() const {
+ return m_state.prev_thread;
+ }
- void OnThreadStart();
+ KThread* GetSchedulerCurrentThread() const {
+ return m_current_thread.load();
+ }
- [[nodiscard]] std::shared_ptr<Common::Fiber>& ControlContext() {
- return switch_fiber;
+ s64 GetLastContextSwitchTime() const {
+ return m_last_context_switch_time;
}
- [[nodiscard]] const std::shared_ptr<Common::Fiber>& ControlContext() const {
- return switch_fiber;
+ // Static public API.
+ static bool CanSchedule(KernelCore& kernel) {
+ return GetCurrentThread(kernel).GetDisableDispatchCount() == 0;
+ }
+ static bool IsSchedulerLockedByCurrentThread(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().scheduler_lock.IsLockedByCurrentThread();
}
- [[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread);
+ static bool IsSchedulerUpdateNeeded(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().scheduler_update_needed;
+ }
+ static void SetSchedulerUpdateNeeded(KernelCore& kernel) {
+ kernel.GlobalSchedulerContext().scheduler_update_needed = true;
+ }
+ static void ClearSchedulerUpdateNeeded(KernelCore& kernel) {
+ kernel.GlobalSchedulerContext().scheduler_update_needed = false;
+ }
- /**
- * Takes a thread and moves it to the back of the it's priority list.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldWithoutCoreMigration(KernelCore& kernel);
+ static void DisableScheduling(KernelCore& kernel);
+ static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
- /**
- * Takes a thread and moves it to the back of the it's priority list.
- * Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or
- * a better priority than the next thread in the core.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldWithCoreMigration(KernelCore& kernel);
-
- /**
- * Takes a thread and moves it out of the scheduling queue.
- * and into the suggested queue. If no thread can be scheduled afterwards in that core,
- * a suggested thread is obtained instead.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldToAnyThread(KernelCore& kernel);
+ static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
static void ClearPreviousThread(KernelCore& kernel, KThread* thread);
- /// Notify the scheduler a thread's status has changed.
static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
-
- /// Notify the scheduler a thread's priority has changed.
static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority);
-
- /// Notify the scheduler a thread's core and/or affinity mask has changed.
static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core);
- static bool CanSchedule(KernelCore& kernel);
- static bool IsSchedulerUpdateNeeded(const KernelCore& kernel);
- static void SetSchedulerUpdateNeeded(KernelCore& kernel);
- static void ClearSchedulerUpdateNeeded(KernelCore& kernel);
- static void DisableScheduling(KernelCore& kernel);
- static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
- [[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
+ static void RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority);
+ static void RescheduleCores(KernelCore& kernel, u64 cores_needing_scheduling);
+
+ static void YieldWithoutCoreMigration(KernelCore& kernel);
+ static void YieldWithCoreMigration(KernelCore& kernel);
+ static void YieldToAnyThread(KernelCore& kernel);
private:
- friend class GlobalSchedulerContext;
-
- /**
- * Takes care of selecting the new scheduled threads in three steps:
- *
- * 1. First a thread is selected from the top of the priority queue. If no thread
- * is obtained then we move to step two, else we are done.
- *
- * 2. Second we try to get a suggested thread that's not assigned to any core or
- * that is not the top thread in that core.
- *
- * 3. Third is no suggested thread is found, we do a second pass and pick a running
- * thread in another core and swap it with its current thread.
- *
- * returns the cores needing scheduling.
- */
- [[nodiscard]] static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
-
- [[nodiscard]] static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel);
-
- void RotateScheduledQueue(s32 cpu_core_id, s32 priority);
+ // Static private API.
+ static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().priority_queue;
+ }
+ static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
- void Schedule();
+ static void RescheduleCurrentHLEThread(KernelCore& kernel);
- /// Switches the CPU's active thread context to that of the specified thread
+ // Instanced private API.
void ScheduleImpl();
+ void ScheduleImplFiber();
+ void SwitchThread(KThread* next_thread);
- /// When a thread wakes up, it must run this through it's new scheduler
- void SwitchContextStep2();
-
- /**
- * Called on every context switch to update the internal timestamp
- * This also updates the running time ticks for the given thread and
- * process using the following difference:
- *
- * ticks += most_recent_ticks - last_context_switch_ticks
- *
- * The internal tick timestamp for the scheduler is simply the
- * most recent tick count retrieved. No special arithmetic is
- * applied to it.
- */
- void UpdateLastContextSwitchTime(KThread* thread, KProcess* process);
-
- void SwitchToCurrent();
+ void Schedule();
+ void ScheduleOnInterrupt();
- KThread* prev_thread{};
- std::atomic<KThread*> current_thread{};
+ void RescheduleOtherCores(u64 cores_needing_scheduling);
+ void RescheduleCurrentCore();
+ void RescheduleCurrentCoreImpl();
- KThread* idle_thread{};
+ u64 UpdateHighestPriorityThread(KThread* thread);
- std::shared_ptr<Common::Fiber> switch_fiber{};
+private:
+ friend class KScopedDisableDispatch;
struct SchedulingState {
- std::atomic<bool> needs_scheduling{};
- bool interrupt_task_thread_runnable{};
- bool should_count_idle{};
- u64 idle_count{};
- KThread* highest_priority_thread{};
- void* idle_thread_stack{};
+ std::atomic<bool> needs_scheduling{false};
+ bool interrupt_task_runnable{false};
+ bool should_count_idle{false};
+ u64 idle_count{0};
+ KThread* highest_priority_thread{nullptr};
+ void* idle_thread_stack{nullptr};
+ std::atomic<KThread*> prev_thread{nullptr};
+ KInterruptTaskManager* interrupt_task_manager{nullptr};
};
- SchedulingState state;
-
- Core::System& system;
- u64 last_context_switch_time{};
- const s32 core_id;
-
- KSpinLock guard{};
+ KernelCore& kernel;
+ SchedulingState m_state;
+ bool m_is_active{false};
+ s32 m_core_id{0};
+ s64 m_last_context_switch_time{0};
+ KThread* m_idle_thread{nullptr};
+ std::atomic<KThread*> m_current_thread{nullptr};
+
+ std::shared_ptr<Common::Fiber> m_switch_fiber{};
+ KThread* m_switch_cur_thread{};
+ KThread* m_switch_highest_priority_thread{};
+ bool m_switch_from_schedule{};
};
-class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> {
+class KScopedSchedulerLock : public KScopedLock<KScheduler::LockType> {
public:
- explicit KScopedSchedulerLock(KernelCore& kernel);
- ~KScopedSchedulerLock();
+ explicit KScopedSchedulerLock(KernelCore& kernel)
+ : KScopedLock(kernel.GlobalSchedulerContext().scheduler_lock) {}
+ ~KScopedSchedulerLock() = default;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index 4fa256970..73314b45e 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -5,9 +5,11 @@
#include <atomic>
#include "common/assert.h"
+#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 50cb5fc90..174afc80d 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -258,7 +258,18 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_
}
Result KThread::InitializeDummyThread(KThread* thread) {
- return thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy);
+ // Initialize the thread.
+ R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy));
+
+ // Initialize emulation parameters.
+ thread->stack_parameters.disable_count = 0;
+
+ return ResultSuccess;
+}
+
+Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) {
+ return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
+ system.GetCpuManager().GetGuestActivateFunc());
}
Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
@@ -277,7 +288,7 @@ Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThr
KProcess* owner) {
system.Kernel().GlobalSchedulerContext().AddThread(thread);
return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner,
- ThreadType::User, system.GetCpuManager().GetGuestThreadStartFunc());
+ ThreadType::User, system.GetCpuManager().GetGuestThreadFunc());
}
void KThread::PostDestroy(uintptr_t arg) {
@@ -308,14 +319,20 @@ void KThread::Finalize() {
auto it = waiter_list.begin();
while (it != waiter_list.end()) {
- // Clear the lock owner
- it->SetLockOwner(nullptr);
+ // Get the thread.
+ KThread* const waiter = std::addressof(*it);
+
+ // The thread shouldn't be a kernel waiter.
+ ASSERT(!IsKernelAddressKey(waiter->GetAddressKey()));
+
+ // Clear the lock owner.
+ waiter->SetLockOwner(nullptr);
// Erase the waiter from our list.
it = waiter_list.erase(it);
// Cancel the thread's wait.
- it->CancelWait(ResultInvalidState, true);
+ waiter->CancelWait(ResultInvalidState, true);
}
}
@@ -1052,6 +1069,8 @@ void KThread::Exit() {
// Register the thread as a work task.
KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this);
}
+
+ UNREACHABLE_MSG("KThread::Exit() would return");
}
Result KThread::Sleep(s64 timeout) {
@@ -1087,6 +1106,8 @@ void KThread::IfDummyThreadTryWait() {
return;
}
+ ASSERT(!kernel.IsPhantomModeForSingleCore());
+
// Block until we are no longer waiting.
std::unique_lock lk(dummy_wait_lock);
dummy_wait_cv.wait(
@@ -1191,16 +1212,13 @@ KScopedDisableDispatch::~KScopedDisableDispatch() {
return;
}
- // Skip the reschedule if single-core, as dispatch tracking is disabled here.
- if (!Settings::values.use_multi_core.GetValue()) {
- return;
- }
-
if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
- auto scheduler = kernel.CurrentScheduler();
+ auto* scheduler = kernel.CurrentScheduler();
- if (scheduler) {
+ if (scheduler && !kernel.IsPhantomModeForSingleCore()) {
scheduler->RescheduleCurrentCore();
+ } else {
+ KScheduler::RescheduleCurrentHLEThread(kernel);
}
} else {
GetCurrentThread(kernel).EnableDispatch();
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 28cd7ecb0..9ee20208e 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -413,6 +413,9 @@ public:
[[nodiscard]] static Result InitializeDummyThread(KThread* thread);
+ [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread,
+ s32 virt_core);
+
[[nodiscard]] static Result InitializeIdleThread(Core::System& system, KThread* thread,
s32 virt_core);
@@ -480,39 +483,16 @@ public:
return per_core_priority_queue_entry[core];
}
- [[nodiscard]] bool IsKernelThread() const {
- return GetActiveCore() == 3;
- }
-
- [[nodiscard]] bool IsDispatchTrackingDisabled() const {
- return is_single_core || IsKernelThread();
- }
-
[[nodiscard]] s32 GetDisableDispatchCount() const {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return 1;
- }
-
return this->GetStackParameters().disable_count;
}
void DisableDispatch() {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return;
- }
-
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
this->GetStackParameters().disable_count++;
}
void EnableDispatch() {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return;
- }
-
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
this->GetStackParameters().disable_count--;
}
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 7307cf262..f4072e1c3 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -64,8 +64,6 @@ struct KernelCore::Impl {
is_phantom_mode_for_singlecore = false;
- InitializePhysicalCores();
-
// Derive the initial memory layout from the emulated board
Init::InitializeSlabResourceCounts(kernel);
DeriveInitialMemoryLayout();
@@ -75,9 +73,9 @@ struct KernelCore::Impl {
InitializeSystemResourceLimit(kernel, system.CoreTiming());
InitializeMemoryLayout();
Init::InitializeKPageBufferSlabHeap(system);
- InitializeSchedulers();
InitializeShutdownThreads();
InitializePreemption(kernel);
+ InitializePhysicalCores();
RegisterHostThread();
}
@@ -95,19 +93,7 @@ struct KernelCore::Impl {
process_list.clear();
- // Close all open server sessions and ports.
- std::unordered_set<KAutoObject*> server_objects_;
- {
- std::scoped_lock lk(server_objects_lock);
- server_objects_ = server_objects;
- server_objects.clear();
- }
- for (auto* server_object : server_objects_) {
- server_object->Close();
- }
-
- // Ensures all service threads gracefully shutdown.
- ClearServiceThreads();
+ CloseServices();
next_object_id = 0;
next_kernel_process_id = KProcess::InitialKIPIDMin;
@@ -148,7 +134,6 @@ struct KernelCore::Impl {
shutdown_threads[core_id] = nullptr;
}
- schedulers[core_id]->Finalize();
schedulers[core_id].reset();
}
@@ -191,18 +176,41 @@ struct KernelCore::Impl {
global_object_list_container.reset();
}
+ void CloseServices() {
+ // Close all open server sessions and ports.
+ std::unordered_set<KAutoObject*> server_objects_;
+ {
+ std::scoped_lock lk(server_objects_lock);
+ server_objects_ = server_objects;
+ server_objects.clear();
+ }
+ for (auto* server_object : server_objects_) {
+ server_object->Close();
+ }
+
+ // Ensures all service threads gracefully shutdown.
+ ClearServiceThreads();
+ }
+
void InitializePhysicalCores() {
exclusive_monitor =
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
- schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i);
+ const s32 core{static_cast<s32>(i)};
+
+ schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel());
cores.emplace_back(i, system, *schedulers[i], interrupts);
- }
- }
- void InitializeSchedulers() {
- for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
- cores[i].Scheduler().Initialize();
+ auto* main_thread{Kernel::KThread::Create(system.Kernel())};
+ main_thread->SetName(fmt::format("MainThread:{}", core));
+ main_thread->SetCurrentCore(core);
+ ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess());
+
+ auto* idle_thread{Kernel::KThread::Create(system.Kernel())};
+ idle_thread->SetCurrentCore(core);
+ ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess());
+
+ schedulers[i]->Initialize(main_thread, idle_thread, core);
}
}
@@ -813,6 +821,10 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
+void KernelCore::CloseServices() {
+ impl->CloseServices();
+}
+
const KResourceLimit* KernelCore::GetSystemResourceLimit() const {
return impl->system_resource_limit;
}
@@ -1101,10 +1113,11 @@ void KernelCore::Suspend(bool suspended) {
}
void KernelCore::ShutdownCores() {
+ KScopedSchedulerLock lk{*this};
+
for (auto* thread : impl->shutdown_threads) {
void(thread->Run());
}
- InterruptAllPhysicalCores();
}
bool KernelCore::IsMulticore() const {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index aa0ebaa02..6c7cf6af2 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -109,6 +109,9 @@ public:
/// Clears all resources in use by the kernel instance.
void Shutdown();
+ /// Close all active services in use by the kernel instance.
+ void CloseServices();
+
/// Retrieves a shared pointer to the system resource limit instance.
const KResourceLimit* GetSystemResourceLimit() const;
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index a5b16ae2e..6e7dacf97 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -43,6 +43,7 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
void PhysicalCore::Run() {
arm_interface->Run();
+ arm_interface->ClearExclusiveState();
}
void PhysicalCore::Idle() {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 8655506b0..27e5a805d 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -887,7 +887,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han
const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
const bool same_thread = current_thread == thread.GetPointerUnsafe();
- const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
+ const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
u64 out_ticks = 0;
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetCpuTime();
@@ -3026,11 +3026,6 @@ void Call(Core::System& system, u32 immediate) {
}
kernel.ExitSVCProfile();
-
- if (!thread->IsCallingSvc()) {
- auto* host_context = thread->GetHostContext().get();
- host_context->Rewind();
- }
}
} // namespace Kernel::Svc
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index b726ac27a..def105832 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -290,7 +290,7 @@ protected:
void Get(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
ProfileBase profile_base{};
- ProfileData data{};
+ UserData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 16};
@@ -373,18 +373,18 @@ protected:
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.RawString());
- if (user_data.size() < sizeof(ProfileData)) {
- LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
+ if (user_data.size() < sizeof(UserData)) {
+ LOG_ERROR(Service_ACC, "UserData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_BUFFER);
return;
}
- ProfileData data;
- std::memcpy(&data, user_data.data(), sizeof(ProfileData));
+ UserData data;
+ std::memcpy(&data, user_data.data(), sizeof(UserData));
if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) {
- LOG_ERROR(Service_ACC, "Failed to update profile data and base!");
+ LOG_ERROR(Service_ACC, "Failed to update user data and base!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_FAILED_SAVE_DATA);
return;
@@ -406,15 +406,15 @@ protected:
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.RawString());
- if (user_data.size() < sizeof(ProfileData)) {
- LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
+ if (user_data.size() < sizeof(UserData)) {
+ LOG_ERROR(Service_ACC, "UserData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_BUFFER);
return;
}
- ProfileData data;
- std::memcpy(&data, user_data.data(), sizeof(ProfileData));
+ UserData data;
+ std::memcpy(&data, user_data.data(), sizeof(UserData));
Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Write,
Common::FS::FileType::BinaryFile);
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 8118ead33..a58da4d5f 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -22,7 +22,7 @@ struct UserRaw {
UUID uuid2{};
u64 timestamp{};
ProfileUsername username{};
- ProfileData extra_data{};
+ UserData extra_data{};
};
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
@@ -263,7 +263,7 @@ UUID ProfileManager::GetLastOpenedUser() const {
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
- ProfileData& data) const {
+ UserData& data) const {
if (GetProfileBase(index, profile)) {
data = profiles[*index].data;
return true;
@@ -272,15 +272,14 @@ bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, Pro
}
/// Return the users profile base and the unknown arbitary data.
-bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
- ProfileData& data) const {
+bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, UserData& data) const {
const auto idx = GetUserIndex(uuid);
return GetProfileBaseAndData(idx, profile, data);
}
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
- ProfileData& data) const {
+ UserData& data) const {
return GetProfileBaseAndData(user.user_uuid, profile, data);
}
@@ -318,7 +317,7 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
}
bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
- const ProfileData& data_new) {
+ const UserData& data_new) {
const auto index = GetUserIndex(uuid);
if (index.has_value() && SetProfileBase(uuid, profile_new)) {
profiles[*index].data = data_new;
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 9940957f1..135f7d0d5 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -22,7 +22,7 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>;
/// Contains extra data related to a user.
/// TODO: RE this structure
-struct ProfileData {
+struct UserData {
INSERT_PADDING_WORDS_NOINIT(1);
u32 icon_id;
u8 bg_color_id;
@@ -30,7 +30,7 @@ struct ProfileData {
INSERT_PADDING_BYTES_NOINIT(0x10);
INSERT_PADDING_BYTES_NOINIT(0x60);
};
-static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size");
+static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
@@ -38,7 +38,7 @@ struct ProfileInfo {
Common::UUID user_uuid{};
ProfileUsername username{};
u64 creation_time{};
- ProfileData data{}; // TODO(ognik): Work out what this is
+ UserData data{}; // TODO(ognik): Work out what this is
bool is_open{};
};
@@ -74,10 +74,9 @@ public:
bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
- ProfileData& data) const;
- bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const;
- bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
- ProfileData& data) const;
+ UserData& data) const;
+ bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, UserData& data) const;
+ bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, UserData& data) const;
std::size_t GetUserCount() const;
std::size_t GetOpenUserCount() const;
bool UserExists(Common::UUID uuid) const;
@@ -93,7 +92,7 @@ public:
bool RemoveUser(Common::UUID uuid);
bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new);
bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
- const ProfileData& data_new);
+ const UserData& data_new);
private:
void ParseUserSaveFile();
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index d35644e73..118f226e4 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -5,7 +5,6 @@
#include <array>
#include <cinttypes>
#include <cstring>
-#include "audio_core/audio_renderer.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
@@ -238,6 +237,7 @@ IDebugFunctions::IDebugFunctions(Core::System& system_)
{130, nullptr, "FriendInvitationSetApplicationParameter"},
{131, nullptr, "FriendInvitationClearApplicationParameter"},
{132, nullptr, "FriendInvitationPushApplicationParameter"},
+ {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"},
{900, nullptr, "GetGrcProcessLaunchedSystemEvent"},
};
// clang-format on
@@ -285,7 +285,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
{64, nullptr, "SetInputDetectionSourceSet"},
- {65, nullptr, "ReportUserIsActive"},
+ {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"},
{66, nullptr, "GetCurrentIlluminance"},
{67, nullptr, "IsIlluminanceAvailable"},
{68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
@@ -517,6 +517,13 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
rb.Push<u32>(idle_time_detection_extension);
}
+void ISelfController::ReportUserIsActive(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
is_auto_sleep_disabled = rp.Pop<bool>();
@@ -635,6 +642,10 @@ void AppletMessageQueue::RequestExit() {
PushMessage(AppletMessage::Exit);
}
+void AppletMessageQueue::RequestResume() {
+ PushMessage(AppletMessage::Resume);
+}
+
void AppletMessageQueue::FocusStateChanged() {
PushMessage(AppletMessage::FocusStateChanged);
}
@@ -1310,6 +1321,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
{34, nullptr, "SelectApplicationLicense"},
{35, nullptr, "GetDeviceSaveDataSizeMax"},
+ {36, nullptr, "GetLimitedApplicationLicense"},
+ {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 988ead215..bb75c6281 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -90,6 +90,7 @@ public:
AppletMessage PopMessage();
std::size_t GetMessageCount() const;
void RequestExit();
+ void RequestResume();
void FocusStateChanged();
void OperationModeChanged();
@@ -174,6 +175,7 @@ private:
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
+ void ReportUserIsActive(Kernel::HLERequestContext& ctx);
void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx);
void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 18d3ae682..48a9a73a0 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -1,68 +1,211 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "audio_core/in/audio_in_system.h"
+#include "audio_core/renderer/audio_device.h"
+#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/audio/audin_u.h"
namespace Service::Audio {
+using namespace AudioCore::AudioIn;
-IAudioIn::IAudioIn(Core::System& system_)
- : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetAudioInState"},
- {1, &IAudioIn::Start, "Start"},
- {2, nullptr, "Stop"},
- {3, nullptr, "AppendAudioInBuffer"},
- {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
- {5, nullptr, "GetReleasedAudioInBuffer"},
- {6, nullptr, "ContainsAudioInBuffer"},
- {7, nullptr, "AppendUacInBuffer"},
- {8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"},
- {9, nullptr, "GetReleasedAudioInBuffersAuto"},
- {10, nullptr, "AppendUacInBufferAuto"},
- {11, nullptr, "GetAudioInBufferCount"},
- {12, nullptr, "SetDeviceGain"},
- {13, nullptr, "GetDeviceGain"},
- {14, nullptr, "FlushAudioInBuffers"},
- };
- // clang-format on
+class IAudioIn final : public ServiceFramework<IAudioIn> {
+public:
+ explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
+ std::string& device_name, const AudioInParameter& in_params, u32 handle,
+ u64 applet_resource_user_id)
+ : ServiceFramework{system_, "IAudioIn"},
+ service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
+ impl{std::make_shared<In>(system_, manager, event, session_id)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IAudioIn::GetAudioInState, "GetAudioInState"},
+ {1, &IAudioIn::Start, "Start"},
+ {2, &IAudioIn::Stop, "Stop"},
+ {3, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBuffer"},
+ {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
+ {5, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffer"},
+ {6, &IAudioIn::ContainsAudioInBuffer, "ContainsAudioInBuffer"},
+ {7, &IAudioIn::AppendAudioInBuffer, "AppendUacInBuffer"},
+ {8, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBufferAuto"},
+ {9, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffersAuto"},
+ {10, &IAudioIn::AppendAudioInBuffer, "AppendUacInBufferAuto"},
+ {11, &IAudioIn::GetAudioInBufferCount, "GetAudioInBufferCount"},
+ {12, &IAudioIn::SetDeviceGain, "SetDeviceGain"},
+ {13, &IAudioIn::GetDeviceGain, "GetDeviceGain"},
+ {14, &IAudioIn::FlushAudioInBuffers, "FlushAudioInBuffers"},
+ };
+ // clang-format on
- RegisterHandlers(functions);
+ RegisterHandlers(functions);
- buffer_event = service_context.CreateEvent("IAudioIn:BufferEvent");
-}
+ if (impl->GetSystem()
+ .Initialize(device_name, in_params, handle, applet_resource_user_id)
+ .IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!");
+ }
+ }
-IAudioIn::~IAudioIn() {
- service_context.CloseEvent(buffer_event);
-}
+ ~IAudioIn() override {
+ impl->Free();
+ service_context.CloseEvent(event);
+ }
-void IAudioIn::Start(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ [[nodiscard]] std::shared_ptr<In> GetImpl() {
+ return impl;
+ }
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
+private:
+ void GetAudioInState(Kernel::HLERequestContext& ctx) {
+ const auto state = static_cast<u32>(impl->GetState());
-void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called. State={}", state);
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
-}
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(state);
+ }
-void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ void Start(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
+ auto result = impl->StartSystem();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void Stop(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto result = impl->StopSystem();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void AppendAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ u64 tag = rp.PopRaw<u64>();
-AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
+ const auto in_buffer_size{ctx.GetReadBufferSize()};
+ if (in_buffer_size < sizeof(AudioInBuffer)) {
+ LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!");
+ }
+
+ const auto& in_buffer = ctx.ReadBuffer();
+ AudioInBuffer buffer{};
+ std::memcpy(&buffer, in_buffer.data(), sizeof(AudioInBuffer));
+
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
+
+ auto result = impl->AppendBuffer(buffer, tag);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto& buffer_event = impl->GetBufferEvent();
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(buffer_event);
+ }
+
+ void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
+ std::vector<u64> released_buffers(write_buffer_size, 0);
+
+ auto count = impl->GetReleasedBuffers(released_buffers);
+
+ [[maybe_unused]] std::string tags{};
+ for (u32 i = 0; i < count; i++) {
+ tags += fmt::format("{:08X}, ", released_buffers[i]);
+ }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
+ tags);
+
+ ctx.WriteBuffer(released_buffers);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(count);
+ }
+
+ void ContainsAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const u64 tag{rp.Pop<u64>()};
+ const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
+
+ LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(buffer_queued);
+ }
+
+ void GetAudioInBufferCount(Kernel::HLERequestContext& ctx) {
+ const auto buffer_count = impl->GetBufferCount();
+
+ LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ rb.Push(ResultSuccess);
+ rb.Push(buffer_count);
+ }
+
+ void SetDeviceGain(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto volume{rp.Pop<f32>()};
+ LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
+
+ impl->SetVolume(volume);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void GetDeviceGain(Kernel::HLERequestContext& ctx) {
+ auto volume{impl->GetVolume()};
+
+ LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(volume);
+ }
+
+ void FlushAudioInBuffers(Kernel::HLERequestContext& ctx) {
+ bool flushed{impl->FlushAudioInBuffers()};
+
+ LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(flushed);
+ }
+
+ KernelHelpers::ServiceContext service_context;
+ Kernel::KEvent* event;
+ std::shared_ptr<AudioCore::AudioIn::In> impl;
+};
+
+AudInU::AudInU(Core::System& system_)
+ : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew},
+ service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
+ system_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &AudInU::ListAudioIns, "ListAudioIns"},
@@ -80,59 +223,152 @@ AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
AudInU::~AudInU() = default;
void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
+
LOG_DEBUG(Service_Audio, "called");
- const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioInDeviceName);
- const std::size_t device_count = std::min(count, audio_device_names.size());
- std::vector<AudioInDeviceName> device_names;
- device_names.reserve(device_count);
+ const auto write_count =
+ static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
+ std::vector<AudioDevice::AudioDeviceName> device_names{};
- for (std::size_t i = 0; i < device_count; i++) {
- const auto& device_name = audio_device_names[i];
- auto& entry = device_names.emplace_back();
- device_name.copy(entry.data(), device_name.size());
+ u32 out_count{0};
+ if (write_count > 0) {
+ out_count = impl->GetDeviceNames(device_names, write_count, false);
+ ctx.WriteBuffer(device_names);
}
- ctx.WriteBuffer(device_names);
-
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(device_names.size()));
+ rb.Push(out_count);
}
void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
+
LOG_DEBUG(Service_Audio, "called");
- constexpr u32 device_count = 0;
- // Since we don't actually use any other audio input devices, we return 0 devices. Filtered
- // device listing just omits the default input device
+ const auto write_count =
+ static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
+ std::vector<AudioDevice::AudioDeviceName> device_names{};
+
+ u32 out_count{0};
+ if (write_count > 0) {
+ out_count = impl->GetDeviceNames(device_names, write_count, true);
+ ctx.WriteBuffer(device_names);
+ }
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(device_count));
+ rb.Push(out_count);
}
-void AudInU::OpenInOutImpl(Kernel::HLERequestContext& ctx) {
- AudInOutParams params{};
- params.channel_count = 2;
- params.sample_format = SampleFormat::PCM16;
- params.sample_rate = 48000;
- params.state = State::Started;
+void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto in_params{rp.PopRaw<AudioInParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
+ const auto device_name_data{ctx.ReadBuffer()};
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
+
+ std::scoped_lock l{impl->mutex};
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
+ }
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+ impl->sessions[new_session_id] = audio_in->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushRaw<AudInOutParams>(params);
- rb.PushIpcInterface<IAudioIn>(system);
-}
-void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
- OpenInOutImpl(ctx);
+ std::string out_name{out_system.GetName()};
+ ctx.WriteBuffer(out_name);
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioInParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioIn>(audio_in);
}
void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
- OpenInOutImpl(ctx);
+ IPC::RequestParser rp{ctx};
+ auto protocol_specified{rp.PopRaw<u64>()};
+ auto in_params{rp.PopRaw<AudioInParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
+ const auto device_name_data{ctx.ReadBuffer()};
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
+
+ std::scoped_lock l{impl->mutex};
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
+ }
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+ impl->sessions[new_session_id] = audio_in->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
+
+ IPC::ResponseBuilder rb{ctx, 6, 0, 1};
+
+ std::string out_name{out_system.GetName()};
+ if (protocol_specified == 0) {
+ if (out_system.IsUac()) {
+ out_name = "UacIn";
+ } else {
+ out_name = "DeviceIn";
+ }
+ }
+
+ ctx.WriteBuffer(out_name);
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioInParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioIn>(audio_in);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h
index 2bfa38ecc..b45fda78a 100644
--- a/src/core/hle/service/audio/audin_u.h
+++ b/src/core/hle/service/audio/audin_u.h
@@ -3,6 +3,8 @@
#pragma once
+#include "audio_core/audio_in_manager.h"
+#include "audio_core/in/audio_in.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
@@ -14,22 +16,12 @@ namespace Kernel {
class HLERequestContext;
}
-namespace Service::Audio {
-
-class IAudioIn final : public ServiceFramework<IAudioIn> {
-public:
- explicit IAudioIn(Core::System& system_);
- ~IAudioIn() override;
-
-private:
- void Start(Kernel::HLERequestContext& ctx);
- void RegisterBufferEvent(Kernel::HLERequestContext& ctx);
- void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx);
-
- KernelHelpers::ServiceContext service_context;
+namespace AudioCore::AudioOut {
+class Manager;
+class In;
+} // namespace AudioCore::AudioOut
- Kernel::KEvent* buffer_event;
-};
+namespace Service::Audio {
class AudInU final : public ServiceFramework<AudInU> {
public:
@@ -37,33 +29,14 @@ public:
~AudInU() override;
private:
- enum class SampleFormat : u32_le {
- PCM16 = 2,
- };
-
- enum class State : u32_le {
- Started = 0,
- Stopped = 1,
- };
-
- struct AudInOutParams {
- u32_le sample_rate{};
- u32_le channel_count{};
- SampleFormat sample_format{};
- State state{};
- };
- static_assert(sizeof(AudInOutParams) == 0x10, "AudInOutParams is an invalid size");
-
- using AudioInDeviceName = std::array<char, 256>;
- static constexpr std::array<std::string_view, 1> audio_device_names{{
- "BuiltInHeadset",
- }};
-
void ListAudioIns(Kernel::HLERequestContext& ctx);
void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx);
void OpenInOutImpl(Kernel::HLERequestContext& ctx);
void OpenAudioIn(Kernel::HLERequestContext& ctx);
void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx);
+
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioCore::AudioIn::Manager> impl;
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index b0dad6053..a44dd842a 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -5,56 +5,43 @@
#include <cstring>
#include <vector>
-#include "audio_core/audio_out.h"
-#include "audio_core/codec.h"
+#include "audio_core/out/audio_out_system.h"
+#include "audio_core/renderer/audio_device.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/audio/audout_u.h"
#include "core/hle/service/audio/errors.h"
-#include "core/hle/service/kernel_helpers.h"
#include "core/memory.h"
namespace Service::Audio {
-
-constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
-constexpr int DefaultSampleRate{48000};
-
-struct AudoutParams {
- s32_le sample_rate;
- u16_le channel_count;
- INSERT_PADDING_BYTES_NOINIT(2);
-};
-static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
-
-enum class AudioState : u32 {
- Started,
- Stopped,
-};
+using namespace AudioCore::AudioOut;
class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
- explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
- AudioCore::AudioOut& audio_core_, std::string&& device_name_,
- std::string&& unique_name)
+ explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
+ size_t session_id, std::string& device_name,
+ const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
: ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
- audio_core{audio_core_}, device_name{std::move(device_name_)},
- audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_,
- "IAudioOut"} {
+ service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(
+ "AudioOutEvent")},
+ impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
+
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
- {1, &IAudioOut::StartAudioOut, "Start"},
- {2, &IAudioOut::StopAudioOut, "Stop"},
- {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
+ {1, &IAudioOut::Start, "Start"},
+ {2, &IAudioOut::Stop, "Stop"},
+ {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"},
{4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
- {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"},
+ {5, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffers"},
{6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
- {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
- {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
+ {7, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBufferAuto"},
+ {8, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffersAuto"},
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"},
{11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
@@ -64,241 +51,263 @@ public:
// clang-format on
RegisterHandlers(functions);
- // This is the event handle used to check if the audio buffer was released
- buffer_event = service_context.CreateEvent("IAudioOutBufferReleased");
-
- stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
- audio_params.channel_count, std::move(unique_name), [this] {
- const auto guard = LockService();
- buffer_event->GetWritableEvent().Signal();
- });
+ if (impl->GetSystem()
+ .Initialize(device_name, in_params, handle, applet_resource_user_id)
+ .IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
+ }
}
~IAudioOut() override {
- service_context.CloseEvent(buffer_event);
+ impl->Free();
+ service_context.CloseEvent(event);
}
-private:
- struct AudioBuffer {
- u64_le next;
- u64_le buffer;
- u64_le buffer_capacity;
- u64_le buffer_size;
- u64_le offset;
- };
- static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
+ [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() {
+ return impl;
+ }
+private:
void GetAudioOutState(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto state = static_cast<u32>(impl->GetState());
+
+ LOG_DEBUG(Service_Audio, "called. State={}", state);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
+ rb.Push(state);
}
- void StartAudioOut(Kernel::HLERequestContext& ctx) {
+ void Start(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
- if (stream->IsPlaying()) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_OPERATION_FAILED);
- return;
- }
-
- audio_core.StartStream(stream);
+ auto result = impl->StartSystem();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
- void StopAudioOut(Kernel::HLERequestContext& ctx) {
+ void Stop(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
- if (stream->IsPlaying()) {
- audio_core.StopStream(stream);
- }
+ auto result = impl->StopSystem();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
- void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
- }
-
- void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
+ void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
+ u64 tag = rp.PopRaw<u64>();
- const auto& input_buffer{ctx.ReadBuffer()};
- ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer),
- "AudioBuffer input is an invalid size!");
- AudioBuffer audio_buffer{};
- std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
- const u64 tag{rp.Pop<u64>()};
+ const auto in_buffer_size{ctx.GetReadBufferSize()};
+ if (in_buffer_size < sizeof(AudioOutBuffer)) {
+ LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!");
+ }
- std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
- main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
+ const auto& in_buffer = ctx.ReadBuffer();
+ AudioOutBuffer buffer{};
+ std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer));
- if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_BUFFER_COUNT_EXCEEDED);
- return;
- }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
+
+ auto result = impl->AppendBuffer(buffer, tag);
IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto& buffer_event = impl->GetBufferEvent();
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
+ rb.PushCopyObjects(buffer_event);
}
- void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
+ void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) {
+ auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
+ std::vector<u64> released_buffers(write_buffer_size, 0);
- const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
- const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
+ auto count = impl->GetReleasedBuffers(released_buffers);
- std::vector<u64> tags{released_buffers};
- tags.resize(max_count);
- ctx.WriteBuffer(tags);
+ [[maybe_unused]] std::string tags{};
+ for (u32 i = 0; i < count; i++) {
+ tags += fmt::format("{:08X}, ", released_buffers[i]);
+ }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
+ tags);
+ ctx.WriteBuffer(released_buffers);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(released_buffers.size()));
+ rb.Push(count);
}
void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
IPC::RequestParser rp{ctx};
+
const u64 tag{rp.Pop<u64>()};
+ const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
+
+ LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->ContainsBuffer(tag));
+ rb.Push(buffer_queued);
}
void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto buffer_count = impl->GetBufferCount();
+
+ LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
IPC::ResponseBuilder rb{ctx, 3};
+
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(stream->GetQueueSize()));
+ rb.Push(buffer_count);
}
void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto samples_played = impl->GetPlayedSampleCount();
+
+ LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played);
IPC::ResponseBuilder rb{ctx, 4};
+
rb.Push(ResultSuccess);
- rb.Push(stream->GetPlayedSampleCount());
+ rb.Push(samples_played);
}
void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ bool flushed{impl->FlushAudioOutBuffers()};
+
+ LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->Flush());
+ rb.Push(flushed);
}
void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const float volume = rp.Pop<float>();
- LOG_DEBUG(Service_Audio, "called, volume={}", volume);
+ const auto volume = rp.Pop<f32>();
+
+ LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
- stream->SetVolume(volume);
+ impl->SetVolume(volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto volume = impl->GetVolume();
+
+ LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->GetVolume());
+ rb.Push(volume);
}
- AudioCore::AudioOut& audio_core;
- AudioCore::StreamPtr stream;
- std::string device_name;
-
- [[maybe_unused]] AudoutParams audio_params{};
-
- Core::Memory::Memory& main_memory;
-
KernelHelpers::ServiceContext service_context;
-
- /// This is the event handle used to check if the audio buffer was released
- Kernel::KEvent* buffer_event;
+ Kernel::KEvent* event;
+ std::shared_ptr<AudioCore::AudioOut::Out> impl;
};
-AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} {
+AudOutU::AudOutU(Core::System& system_)
+ : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew},
+ service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>(
+ system_)} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
- {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
- {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
- {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"},
+ {0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
+ {1, &AudOutU::OpenAudioOut, "OpenAudioOut"},
+ {2, &AudOutU::ListAudioOuts, "ListAudioOutsAuto"},
+ {3, &AudOutU::OpenAudioOut, "OpenAudioOutAuto"},
};
// clang-format on
RegisterHandlers(functions);
- audio_core = std::make_unique<AudioCore::AudioOut>();
}
AudOutU::~AudOutU() = default;
-void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
- ctx.WriteBuffer(DefaultDevice);
+ std::scoped_lock l{impl->mutex};
+
+ const auto write_count =
+ static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
+ std::vector<AudioDevice::AudioDeviceName> device_names{};
+ std::string print_names{};
+ if (write_count > 0) {
+ device_names.push_back(AudioDevice::AudioDeviceName("DeviceOut"));
+ LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut");
+ } else {
+ LOG_DEBUG(Service_Audio, "called. Empty buffer passed in.");
+ }
+
+ ctx.WriteBuffer(device_names);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(1); // Amount of audio devices
+ rb.Push<u32>(static_cast<u32>(device_names.size()));
}
-void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
+void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto in_params{rp.PopRaw<AudioOutParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
const auto device_name_data{ctx.ReadBuffer()};
- std::string device_name;
- if (device_name_data[0] != '\0') {
- device_name.assign(device_name_data.begin(), device_name_data.end());
- } else {
- device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
- }
- ctx.WriteBuffer(device_name);
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
- IPC::RequestParser rp{ctx};
- auto params{rp.PopRaw<AudoutParams>()};
- if (params.channel_count <= 2) {
- // Mono does not exist for audout
- params.channel_count = 2;
- } else {
- params.channel_count = 6;
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
}
- if (!params.sample_rate) {
- params.sample_rate = DefaultSampleRate;
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
}
- std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
- auto audio_out_interface = std::make_shared<IAudioOut>(
- system, params, *audio_core, std::move(device_name), std::move(unique_name));
+ LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+
+ impl->sessions[new_session_id] = audio_out->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioOutParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
- rb.Push(ResultSuccess);
- rb.Push<u32>(DefaultSampleRate);
- rb.Push<u32>(params.channel_count);
- rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
- rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
- rb.PushIpcInterface<IAudioOut>(audio_out_interface);
- audio_out_interfaces.push_back(std::move(audio_out_interface));
+ ctx.WriteBuffer(out_system.GetName());
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioOutParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioOut>(audio_out);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index d82004c2e..fdc0ee754 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -3,13 +3,11 @@
#pragma once
-#include <vector>
+#include "audio_core/audio_out_manager.h"
+#include "audio_core/out/audio_out.h"
+#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
-namespace AudioCore {
-class AudioOut;
-}
-
namespace Core {
class System;
}
@@ -18,6 +16,11 @@ namespace Kernel {
class HLERequestContext;
}
+namespace AudioCore::AudioOut {
+class Manager;
+class Out;
+} // namespace AudioCore::AudioOut
+
namespace Service::Audio {
class IAudioOut;
@@ -28,11 +31,11 @@ public:
~AudOutU() override;
private:
- void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
- void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
+ void ListAudioOuts(Kernel::HLERequestContext& ctx);
+ void OpenAudioOut(Kernel::HLERequestContext& ctx);
- std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
- std::unique_ptr<AudioCore::AudioOut> audio_core;
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioCore::AudioOut::Manager> impl;
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 2ce63c004..381a66ba5 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -4,7 +4,12 @@
#include <array>
#include <memory>
-#include "audio_core/audio_renderer.h"
+#include "audio_core/audio_core.h"
+#include "audio_core/common/audio_renderer_parameter.h"
+#include "audio_core/common/feature_support.h"
+#include "audio_core/renderer/audio_device.h"
+#include "audio_core/renderer/audio_renderer.h"
+#include "audio_core/renderer/voice/voice_info.h"
#include "common/alignment.h"
#include "common/bit_util.h"
#include "common/common_funcs.h"
@@ -13,91 +18,112 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/audio/audren_u.h"
#include "core/hle/service/audio/errors.h"
+#include "core/memory.h"
+
+using namespace AudioCore::AudioRenderer;
namespace Service::Audio {
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
- explicit IAudioRenderer(Core::System& system_,
- const AudioCommon::AudioRendererParameter& audren_params,
- const std::size_t instance_number)
+ explicit IAudioRenderer(Core::System& system_, Manager& manager_,
+ AudioCore::AudioRendererParameterInternal& params,
+ Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
+ u32 process_handle, u64 applet_resource_user_id, s32 session_id)
: ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
- service_context{system_, "IAudioRenderer"} {
+ service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent(
+ "IAudioRendererEvent")},
+ manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
{3, &IAudioRenderer::GetState, "GetState"},
- {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
+ {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
{5, &IAudioRenderer::Start, "Start"},
{6, &IAudioRenderer::Stop, "Stop"},
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
{8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
- {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
- {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"},
+ {10, nullptr, "RequestUpdateAuto"},
+ {11, nullptr, "ExecuteAudioRendererRendering"},
};
// clang-format on
RegisterHandlers(functions);
- system_event = service_context.CreateEvent("IAudioRenderer:SystemEvent");
- renderer = std::make_unique<AudioCore::AudioRenderer>(
- system.CoreTiming(), system.Memory(), audren_params,
- [this]() {
- const auto guard = LockService();
- system_event->GetWritableEvent().Signal();
- },
- instance_number);
+ impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle,
+ applet_resource_user_id, session_id);
}
~IAudioRenderer() override {
- service_context.CloseEvent(system_event);
+ impl->Finalize();
+ service_context.CloseEvent(rendered_event);
}
private:
void GetSampleRate(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto sample_rate{impl->GetSystem().GetSampleRate()};
+
+ LOG_DEBUG(Service_Audio, "called. Sample rate {}", sample_rate);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetSampleRate());
+ rb.Push(sample_rate);
}
void GetSampleCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto sample_count{impl->GetSystem().GetSampleCount()};
+
+ LOG_DEBUG(Service_Audio, "called. Sample count {}", sample_count);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetSampleCount());
+ rb.Push(sample_count);
}
void GetState(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const u32 state{!impl->GetSystem().IsActive()};
+
+ LOG_DEBUG(Service_Audio, "called, state {}", state);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
+ rb.Push(state);
}
void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+ const auto buffer_count{impl->GetSystem().GetMixBufferCount()};
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetMixBufferCount());
+ rb.Push(buffer_count);
}
- void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "(STUBBED) called");
+ void RequestUpdate(Kernel::HLERequestContext& ctx) {
+ LOG_TRACE(Service_Audio, "called");
- std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0);
- auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
+ std::vector<u8> input{ctx.ReadBuffer(0)};
+
+ // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
+ // checking size 0. Performance size is 0 for most games.
+ const auto buffers{ctx.BufferDescriptorB()};
+ std::vector<u8> output(buffers[0].Size(), 0);
+ std::vector<u8> performance(buffers[1].Size(), 0);
+
+ auto result = impl->RequestUpdate(input, performance, output);
if (result.IsSuccess()) {
- ctx.WriteBuffer(output_params);
+ ctx.WriteBufferB(output.data(), output.size(), 0);
+ ctx.WriteBufferB(performance.data(), performance.size(), 1);
+ } else {
+ LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
}
IPC::ResponseBuilder rb{ctx, 2};
@@ -105,38 +131,45 @@ private:
}
void Start(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
- const auto result = renderer->Start();
+ impl->Start();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void Stop(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
- const auto result = renderer->Stop();
+ impl->Stop();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
+
+ if (impl->GetSystem().GetExecutionMode() == AudioCore::ExecutionMode::Manual) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_NOT_SUPPORTED);
+ return;
+ }
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(system_event->GetReadableEvent());
+ rb.PushCopyObjects(rendered_event->GetReadableEvent());
}
void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
IPC::RequestParser rp{ctx};
- rendering_time_limit_percent = rp.Pop<u32>();
- LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
- rendering_time_limit_percent);
+ auto limit = rp.PopRaw<u32>();
- ASSERT(rendering_time_limit_percent <= 100);
+ auto& system_ = impl->GetSystem();
+ system_.SetRenderingTimeLimit(limit);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -145,34 +178,34 @@ private:
void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+ auto& system_ = impl->GetSystem();
+ auto time = system_.GetRenderingTimeLimit();
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(rendering_time_limit_percent);
+ rb.Push(time);
}
void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
-
- // This service command currently only reports an unsupported operation
- // error code, or aborts. Given that, we just always return an error
- // code in this case.
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_NOT_SUPPORTED);
}
KernelHelpers::ServiceContext service_context;
-
- Kernel::KEvent* system_event;
- std::unique_ptr<AudioCore::AudioRenderer> renderer;
- u32 rendering_time_limit_percent = 100;
+ Kernel::KEvent* rendered_event;
+ Manager& manager;
+ std::unique_ptr<Renderer> impl;
};
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
+
public:
- explicit IAudioDevice(Core::System& system_, Kernel::KEvent* buffer_event_, u32_le revision_)
- : ServiceFramework{system_, "IAudioDevice"}, buffer_event{buffer_event_}, revision{
- revision_} {
+ explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
+ u32 device_num)
+ : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew},
+ service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>(
+ system_, applet_resource_user_id,
+ revision)},
+ event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
static const FunctionInfo functions[] = {
{0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
{1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
@@ -186,54 +219,45 @@ public:
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
{11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"},
{12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
- {13, nullptr, "GetActiveAudioOutputDeviceName"},
- {14, nullptr, "ListAudioOutputDeviceName"},
+ {13, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioOutputDeviceName"},
+ {14, &IAudioDevice::ListAudioOutputDeviceName, "ListAudioOutputDeviceName"},
};
RegisterHandlers(functions);
+
+ event->GetWritableEvent().Signal();
}
-private:
- using AudioDeviceName = std::array<char, 256>;
- static constexpr std::array<std::string_view, 4> audio_device_names{{
- "AudioStereoJackOutput",
- "AudioBuiltInSpeakerOutput",
- "AudioTvOutput",
- "AudioUsbDeviceOutput",
- }};
- enum class DeviceType {
- AHUBHeadphones,
- AHUBSpeakers,
- HDA,
- USBOutput,
- };
+ ~IAudioDevice() override {
+ service_context.CloseEvent(event);
+ }
+private:
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
- const bool usb_output_supported =
- IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision);
- const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName);
+ std::vector<AudioDevice::AudioDeviceName> out_names{};
- std::vector<AudioDeviceName> name_buffer;
- name_buffer.reserve(audio_device_names.size());
+ u32 out_count = impl->ListAudioDeviceName(out_names, in_count);
- for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) {
- const auto type = static_cast<DeviceType>(i);
-
- if (!usb_output_supported && type == DeviceType::USBOutput) {
- continue;
+ std::string out{};
+ for (u32 i = 0; i < out_count; i++) {
+ std::string a{};
+ u32 j = 0;
+ while (out_names[i].name[j] != '\0') {
+ a += out_names[i].name[j];
+ j++;
}
-
- const auto& device_name = audio_device_names[i];
- auto& entry = name_buffer.emplace_back();
- device_name.copy(entry.data(), device_name.size());
+ out += "\n\t" + a;
}
- ctx.WriteBuffer(name_buffer);
+ LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
IPC::ResponseBuilder rb{ctx, 3};
+
+ ctx.WriteBuffer(out_names);
+
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(name_buffer.size()));
+ rb.Push(out_count);
}
void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
@@ -243,7 +267,11 @@ private:
const auto device_name_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(device_name_buffer);
- LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume);
+ LOG_DEBUG(Service_Audio, "called. name={}, volume={}", name, volume);
+
+ if (name == "AudioTvOutput") {
+ impl->SetDeviceVolumes(volume);
+ }
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -253,53 +281,60 @@ private:
const auto device_name_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(device_name_buffer);
- LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name);
+ LOG_DEBUG(Service_Audio, "called. Name={}", name);
+
+ f32 volume{1.0f};
+ if (name == "AudioTvOutput") {
+ volume = impl->GetDeviceVolume(name);
+ }
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(1.0f);
+ rb.Push(volume);
}
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ const auto write_size = ctx.GetWriteBufferSize() / sizeof(char);
+ std::string out_name{"AudioTvOutput"};
- // Currently set to always be TV audio output.
- const auto& device_name = audio_device_names[2];
+ LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name);
- AudioDeviceName out_device_name{};
- device_name.copy(out_device_name.data(), device_name.size());
+ out_name.resize(write_size);
- ctx.WriteBuffer(out_device_name);
+ ctx.WriteBuffer(out_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "(STUBBED) called");
- buffer_event->GetWritableEvent().Signal();
+ event->GetWritableEvent().Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ const auto& sink{system.AudioCore().GetOutputSink()};
+ u32 channel_count{sink.GetDeviceChannels()};
+
+ LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count);
IPC::ResponseBuilder rb{ctx, 3};
+
rb.Push(ResultSuccess);
- rb.Push<u32>(2);
+ rb.Push<u32>(channel_count);
}
- // Should be similar to QueryAudioDeviceOutputEvent
void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) {
@@ -307,402 +342,167 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
- Kernel::KEvent* buffer_event;
- u32_le revision = 0;
+ void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) {
+ const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
+
+ std::vector<AudioDevice::AudioDeviceName> out_names{};
+
+ u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count);
+
+ std::string out{};
+ for (u32 i = 0; i < out_count; i++) {
+ std::string a{};
+ u32 j = 0;
+ while (out_names[i].name[j] != '\0') {
+ a += out_names[i].name[j];
+ j++;
+ }
+ out += "\n\t" + a;
+ }
+
+ LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ ctx.WriteBuffer(out_names);
+
+ rb.Push(ResultSuccess);
+ rb.Push(out_count);
+ }
+
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioDevice> impl;
+ Kernel::KEvent* event;
};
AudRenU::AudRenU(Core::System& system_)
- : ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"} {
+ : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew},
+ service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
- {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetWorkBufferSize"},
+ {1, &AudRenU::GetWorkBufferSize, "GetWorkBufferSize"},
{2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
- {3, &AudRenU::OpenAudioRendererForManualExecution, "OpenAudioRendererForManualExecution"},
+ {3, nullptr, "OpenAudioRendererForManualExecution"},
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
};
// clang-format on
RegisterHandlers(functions);
-
- buffer_event = service_context.CreateEvent("IAudioOutBufferReleasedEvent");
}
-AudRenU::~AudRenU() {
- service_context.CloseEvent(buffer_event);
-}
+AudRenU::~AudRenU() = default;
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- OpenAudioRendererImpl(ctx);
-}
-
-static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) {
- // +1 represents the final mix.
- return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
- 1;
-}
-
-void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- // Several calculations below align the sizes being calculated
- // onto a 64 byte boundary.
- static constexpr u64 buffer_alignment_size = 64;
-
- // Some calculations that calculate portions of the buffer
- // that will contain information, on the other hand, align
- // the result of some of their calcularions on a 16 byte boundary.
- static constexpr u64 info_field_alignment_size = 16;
-
- // Maximum detail entries that may exist at one time for performance
- // frame statistics.
- static constexpr u64 max_perf_detail_entries = 100;
-
- // Size of the data structure representing the bulk of the voice-related state.
- static constexpr u64 voice_state_size_bytes = 0x100;
-
- // Size of the upsampler manager data structure
- constexpr u64 upsampler_manager_size = 0x48;
-
- // Calculates the part of the size that relates to mix buffers.
- const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) {
- // As of 8.0.0 this is the maximum on voice channels.
- constexpr u64 max_voice_channels = 6;
-
- // The service expects the sample_count member of the parameters to either be
- // a value of 160 or 240, so the maximum sample count is assumed in order
- // to adequately handle all values at runtime.
- constexpr u64 default_max_sample_count = 240;
-
- const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels;
-
- u64 size = 0;
- size += total_mix_buffers * (sizeof(s32) * params.sample_count);
- size += total_mix_buffers * (sizeof(s32) * default_max_sample_count);
- size += u64{params.submix_count} + params.sink_count;
- size = Common::AlignUp(size, buffer_alignment_size);
- size += Common::AlignUp(params.unknown_30, buffer_alignment_size);
- size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size);
- return size;
- };
-
- // Calculates the portion of the size related to the mix data (and the sorting thereof).
- const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- // The size of the mixing info data structure.
- constexpr u64 mix_info_size = 0x940;
-
- // Consists of total submixes with the final mix included.
- const u64 total_mix_count = u64{params.submix_count} + 1;
-
- // The total number of effects that may be available to the audio renderer at any time.
- constexpr u64 max_effects = 256;
-
- // Calculates the part of the size related to the audio node state.
- // This will only be used if the audio revision supports the splitter.
- const auto calculate_node_state_size = [](std::size_t num_nodes) {
- // Internally within a nodestate, it appears to use a data structure
- // similar to a std::bitset<64> twice.
- constexpr u64 bit_size = Common::BitSize<u64>();
- constexpr u64 num_bitsets = 2;
-
- // Node state instances have three states internally for performing
- // depth-first searches of nodes. Initialized, Found, and Done Sorting.
- constexpr u64 num_states = 3;
-
- u64 size = 0;
- size += (num_nodes * num_nodes) * sizeof(s32);
- size += num_states * (num_nodes * sizeof(s32));
- size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>());
- return size;
- };
-
- // Calculates the part of the size related to the adjacency (aka edge) matrix.
- const auto calculate_edge_matrix_size = [](std::size_t num_nodes) {
- return (num_nodes * num_nodes) * sizeof(s32);
- };
-
- u64 size = 0;
- size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size);
- size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size);
- size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count,
- info_field_alignment_size);
-
- if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
- size += Common::AlignUp(calculate_node_state_size(total_mix_count) +
- calculate_edge_matrix_size(total_mix_count),
- info_field_alignment_size);
- }
-
- return size;
- };
-
- // Calculates the part of the size related to voice channel info.
- const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 voice_info_size = 0x220;
- constexpr u64 voice_resource_size = 0xD0;
-
- u64 size = 0;
- size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size);
- size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size);
- size +=
- Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size);
- size +=
- Common::AlignUp(voice_state_size_bytes * params.voice_count, info_field_alignment_size);
- return size;
- };
-
- // Calculates the part of the size related to memory pools.
- const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
- const u64 memory_pool_info_size = 0x20;
- return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to the splitter context.
- const auto calculate_splitter_context_size =
- [](const AudioCommon::AudioRendererParameter& params) -> u64 {
- if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
- return 0;
- }
-
- constexpr u64 splitter_info_size = 0x20;
- constexpr u64 splitter_destination_data_size = 0xE0;
-
- u64 size = 0;
- size += params.num_splitter_send_channels;
- size +=
- Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size);
- size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels,
- info_field_alignment_size);
-
- return size;
- };
-
- // Calculates the part of the size related to the upsampler info.
- const auto calculate_upsampler_info_size =
- [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 upsampler_info_size = 0x280;
- // Yes, using the buffer size over info alignment size is intentional here.
- return Common::AlignUp(upsampler_info_size *
- (u64{params.submix_count} + params.sink_count),
- buffer_alignment_size);
- };
-
- // Calculates the part of the size related to effect info.
- const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 effect_info_size = 0x2B0;
- return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to audio sink info.
- const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 sink_info_size = 0x170;
- return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to voice state info.
- const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 voice_state_size = 0x100;
- const u64 additional_size = buffer_alignment_size - 1;
- return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
- info_field_alignment_size);
- };
-
- // Calculates the part of the size related to performance statistics.
- const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) {
- // Extra size value appended to the end of the calculation.
- constexpr u64 appended = 128;
-
- // Whether or not we assume the newer version of performance metrics data structures.
- const bool is_v2 =
- IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision);
-
- // Data structure sizes
- constexpr u64 perf_statistics_size = 0x0C;
- const u64 header_size = is_v2 ? 0x30 : 0x18;
- const u64 entry_size = is_v2 ? 0x18 : 0x10;
- const u64 detail_size = is_v2 ? 0x18 : 0x10;
-
- const u64 entry_count = CalculateNumPerformanceEntries(params);
- const u64 size_per_frame =
- header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries);
-
- u64 size = 0;
- size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1,
- buffer_alignment_size);
- size += Common::AlignUp(perf_statistics_size, buffer_alignment_size);
- size += appended;
- return size;
- };
-
- // Calculates the part of the size that relates to the audio command buffer.
- const auto calculate_command_buffer_size =
- [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
-
- if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
- constexpr u64 command_buffer_size = 0x18000;
-
- return command_buffer_size + alignment;
- }
-
- // When the variadic command buffer is supported, this means
- // the command generator for the audio renderer can issue commands
- // that are (as one would expect), variable in size. So what we need to do
- // is determine the maximum possible size for a few command data structures
- // then multiply them by the amount of present commands indicated by the given
- // respective audio parameters.
-
- constexpr u64 max_biquad_filters = 2;
- constexpr u64 max_mix_buffers = 24;
-
- constexpr u64 biquad_filter_command_size = 0x2C;
-
- constexpr u64 depop_mix_command_size = 0x24;
- constexpr u64 depop_setup_command_size = 0x50;
-
- constexpr u64 effect_command_max_size = 0x540;
-
- constexpr u64 mix_command_size = 0x1C;
- constexpr u64 mix_ramp_command_size = 0x24;
- constexpr u64 mix_ramp_grouped_command_size = 0x13C;
-
- constexpr u64 perf_command_size = 0x28;
-
- constexpr u64 sink_command_size = 0x130;
-
- constexpr u64 submix_command_max_size =
- depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
-
- constexpr u64 volume_command_size = 0x1C;
- constexpr u64 volume_ramp_command_size = 0x20;
-
- constexpr u64 voice_biquad_filter_command_size =
- biquad_filter_command_size * max_biquad_filters;
- constexpr u64 voice_data_command_size = 0x9C;
- const u64 voice_command_max_size =
- (params.splitter_count * depop_setup_command_size) +
- (voice_data_command_size + voice_biquad_filter_command_size +
- volume_ramp_command_size + mix_ramp_grouped_command_size);
+ IPC::RequestParser rp{ctx};
- // Now calculate the individual elements that comprise the size and add them together.
- const u64 effect_commands_size = params.effect_count * effect_command_max_size;
+ AudioCore::AudioRendererParameterInternal params;
+ rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
+ auto transfer_memory_handle = ctx.GetCopyHandle(0);
+ auto process_handle = ctx.GetCopyHandle(1);
+ auto transfer_memory_size = rp.Pop<u64>();
+ auto applet_resource_user_id = rp.Pop<u64>();
- const u64 final_mix_commands_size =
- depop_mix_command_size + volume_command_size * max_mix_buffers;
+ if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) {
+ LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
+ return;
+ }
- const u64 perf_commands_size =
- perf_command_size *
- (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
+ const auto& handle_table{system.CurrentProcess()->GetHandleTable()};
+ auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
+ auto transfer_memory{
+ process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
- const u64 sink_commands_size = params.sink_count * sink_command_size;
+ const auto session_id{impl->GetSessionId()};
+ if (session_id == -1) {
+ LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
+ return;
+ }
- const u64 splitter_commands_size =
- params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
+ LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id,
+ impl->GetSessionCount());
- const u64 submix_commands_size = params.submix_count * submix_command_max_size;
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(),
+ transfer_memory_size, process_handle,
+ applet_resource_user_id, session_id);
+}
- const u64 voice_commands_size = params.voice_count * voice_command_max_size;
-
- return effect_commands_size + final_mix_commands_size + perf_commands_size +
- sink_commands_size + splitter_commands_size + submix_commands_size +
- voice_commands_size + alignment;
- };
+void AudRenU::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
+ AudioCore::AudioRendererParameterInternal params;
IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
-
- u64 size = 0;
- size += calculate_mix_buffer_sizes(params);
- size += calculate_mix_info_size(params);
- size += calculate_voice_info_size(params);
- size += upsampler_manager_size;
- size += calculate_memory_pools_size(params);
- size += calculate_splitter_context_size(params);
-
- size = Common::AlignUp(size, buffer_alignment_size);
-
- size += calculate_upsampler_info_size(params);
- size += calculate_effect_info_size(params);
- size += calculate_sink_info_size(params);
- size += calculate_voice_state_size(params);
- size += calculate_perf_size(params);
- size += calculate_command_buffer_size(params);
-
- // finally, 4KB page align the size, and we're done.
- size = Common::AlignUp(size, 4096);
+ rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
+
+ u64 size{0};
+ auto result = impl->GetWorkBufferSize(params, size);
+
+ std::string output_info{};
+ output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision));
+ output_info +=
+ fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count);
+ output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}",
+ static_cast<u32>(params.execution_mode), params.voice_drop_enabled);
+ output_info += fmt::format(
+ "\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos "
+ "{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External "
+ "Context {:04X}",
+ params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos,
+ params.splitter_destinations, params.voices, params.perf_frames,
+ params.external_context_size);
+
+ LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}",
+ output_info, size);
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
+ rb.Push(result);
rb.Push<u64>(size);
-
- LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size);
}
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const u64 aruid = rp.Pop<u64>();
- LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid);
+ const auto applet_resource_user_id = rp.Pop<u64>();
+
+ LOG_DEBUG(Service_Audio, "called. Applet resource id {}", applet_resource_user_id);
- // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that
- // always assumes the initial release revision (REV1).
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioDevice>(system, buffer_event, Common::MakeMagic('R', 'E', 'V', '1'));
+ rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id,
+ ::Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++);
}
void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
-
- OpenAudioRendererImpl(ctx);
}
void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
struct Parameters {
u32 revision;
- u64 aruid;
+ u64 applet_resource_user_id;
};
IPC::RequestParser rp{ctx};
- const auto [revision, aruid] = rp.PopRaw<Parameters>();
- LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid);
+ const auto [revision, applet_resource_user_id] = rp.PopRaw<Parameters>();
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioDevice>(system, buffer_event, revision);
-}
+ LOG_DEBUG(Service_Audio, "called. Revision {} Applet resource id {}",
+ AudioCore::GetRevisionNum(revision), applet_resource_user_id);
-void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++);
-}
-
-bool IsFeatureSupported(AudioFeatures feature, u32_le revision) {
- // Byte swap
- const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
-
- switch (feature) {
- case AudioFeatures::AudioUSBDeviceOutput:
- return version_num >= 4U;
- case AudioFeatures::Splitter:
- return version_num >= 2U;
- case AudioFeatures::PerformanceMetricsVersion2:
- case AudioFeatures::VariadicCommandBuffer:
- return version_num >= 5U;
- default:
- return false;
- }
+ rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, revision,
+ num_audio_devices++);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index 869d39002..4384a9b3c 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -3,6 +3,7 @@
#pragma once
+#include "audio_core/audio_render_manager.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
@@ -15,6 +16,7 @@ class HLERequestContext;
}
namespace Service::Audio {
+class IAudioRenderer;
class AudRenU final : public ServiceFramework<AudRenU> {
public:
@@ -23,28 +25,14 @@ public:
private:
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
- void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
+ void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
void GetAudioDeviceService(Kernel::HLERequestContext& ctx);
void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx);
void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx);
- void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx);
-
KernelHelpers::ServiceContext service_context;
-
- std::size_t audren_instance_count = 0;
- Kernel::KEvent* buffer_event;
+ std::unique_ptr<AudioCore::AudioRenderer::Manager> impl;
+ u32 num_audio_devices{0};
};
-// Describes a particular audio feature that may be supported in a particular revision.
-enum class AudioFeatures : u32 {
- AudioUSBDeviceOutput,
- Splitter,
- PerformanceMetricsVersion2,
- VariadicCommandBuffer,
-};
-
-// Tests if a particular audio feature is supported with a given audio revision.
-bool IsFeatureSupported(AudioFeatures feature, u32_le revision);
-
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h
index ac6c514af..d706978cb 100644
--- a/src/core/hle/service/audio/errors.h
+++ b/src/core/hle/service/audio/errors.h
@@ -7,8 +7,17 @@
namespace Service::Audio {
+constexpr Result ERR_INVALID_DEVICE_NAME{ErrorModule::Audio, 1};
constexpr Result ERR_OPERATION_FAILED{ErrorModule::Audio, 2};
+constexpr Result ERR_INVALID_SAMPLE_RATE{ErrorModule::Audio, 3};
+constexpr Result ERR_INSUFFICIENT_BUFFER_SIZE{ErrorModule::Audio, 4};
+constexpr Result ERR_MAXIMUM_SESSIONS_REACHED{ErrorModule::Audio, 5};
constexpr Result ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8};
+constexpr Result ERR_INVALID_CHANNEL_COUNT{ErrorModule::Audio, 10};
+constexpr Result ERR_INVALID_UPDATE_DATA{ErrorModule::Audio, 41};
+constexpr Result ERR_POOL_MAPPING_FAILED{ErrorModule::Audio, 42};
constexpr Result ERR_NOT_SUPPORTED{ErrorModule::Audio, 513};
+constexpr Result ERR_INVALID_PROCESS_HANDLE{ErrorModule::Audio, 1536};
+constexpr Result ERR_INVALID_REVISION{ErrorModule::Audio, 1537};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 75da659e5..4f2ed2d52 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -298,7 +298,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) {
const auto sample_rate = rp.Pop<u32>();
const auto channel_count = rp.Pop<u32>();
- LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
+ LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
sample_rate == 12000 || sample_rate == 8000,
diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp
index d928e37fb..bc08ac487 100644
--- a/src/core/hle/service/bcat/bcat_module.cpp
+++ b/src/core/hle/service/bcat/bcat_module.cpp
@@ -140,8 +140,8 @@ public:
{20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
{20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
{30100, &IBcatService::SetPassphrase, "SetPassphrase"},
- {30101, nullptr, "Unknown"},
- {30102, nullptr, "Unknown2"},
+ {30101, nullptr, "Unknown30101"},
+ {30102, nullptr, "Unknown30102"},
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index f9ee2b624..ec7e5320c 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -181,6 +181,11 @@ public:
{147, nullptr, "RegisterAudioControlNotification"},
{148, nullptr, "SendAudioControlPassthroughCommand"},
{149, nullptr, "SendAudioControlSetAbsoluteVolumeCommand"},
+ {150, nullptr, "AcquireAudioSinkVolumeLocallyChangedEvent"},
+ {151, nullptr, "AcquireAudioSinkVolumeUpdateRequestCompletedEvent"},
+ {152, nullptr, "GetAudioSinkVolume"},
+ {153, nullptr, "RequestUpdateAudioSinkVolume"},
+ {154, nullptr, "IsAudioSinkVolumeSupported"},
{256, nullptr, "IsManufacturingMode"},
{257, nullptr, "EmulateBluetoothCrash"},
{258, nullptr, "GetBleChannelMap"},
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 3fa88cbd3..eebf85e03 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -214,8 +214,12 @@ public:
{76, nullptr, "Unknown76"},
{100, nullptr, "Unknown100"},
{101, nullptr, "Unknown101"},
- {110, nullptr, "Unknown102"},
- {111, nullptr, "Unknown103"},
+ {110, nullptr, "Unknown110"},
+ {111, nullptr, "Unknown111"},
+ {112, nullptr, "Unknown112"},
+ {113, nullptr, "Unknown113"},
+ {114, nullptr, "Unknown114"},
+ {115, nullptr, "Unknown115"},
};
// clang-format on
diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp
index 7d35b4208..4a81bb5e2 100644
--- a/src/core/hle/service/fatal/fatal_p.cpp
+++ b/src/core/hle/service/fatal/fatal_p.cpp
@@ -6,7 +6,13 @@
namespace Service::Fatal {
Fatal_P::Fatal_P(std::shared_ptr<Module> module_, Core::System& system_)
- : Interface(std::move(module_), system_, "fatal:p") {}
+ : Interface(std::move(module_), system_, "fatal:p") {
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetFatalEvent"},
+ {10, nullptr, "GetFatalContext"},
+ };
+ RegisterHandlers(functions);
+}
Fatal_P::~Fatal_P() = default;
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index aa7189bda..3c28dee76 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -838,11 +838,11 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
const auto now = steady_clock::now();
- // Filter out non-zero vibrations that are within 10ms of each other.
+ // Filter out non-zero vibrations that are within 15ms of each other.
if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
duration_cast<milliseconds>(
now - controller.vibration[device_index].last_vibration_timepoint) <
- milliseconds(10)) {
+ milliseconds(15)) {
return false;
}
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 46282f42e..4613a4e60 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -19,3 +19,10 @@ constexpr Result InvalidNpadId{ErrorModule::HID, 709};
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
} // namespace Service::HID
+
+namespace Service::IRS {
+
+constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
+constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 89bb12442..5ecbddf94 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2345,8 +2345,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
std::make_shared<HidSys>(system)->InstallAsService(service_manager);
std::make_shared<HidTmp>(system)->InstallAsService(service_manager);
- std::make_shared<IRS>(system)->InstallAsService(service_manager);
- std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager);
+ std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager);
+ std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager);
std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager);
}
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index d2a91d913..d5107e41f 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -1,16 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <algorithm>
+#include <random>
+
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/irs.h"
+#include "core/hle/service/hid/irsensor/clustering_processor.h"
+#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
+#include "core/hle/service/hid/irsensor/ir_led_processor.h"
+#include "core/hle/service/hid/irsensor/moment_processor.h"
+#include "core/hle/service/hid/irsensor/pointing_processor.h"
+#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
+#include "core/memory.h"
-namespace Service::HID {
+namespace Service::IRS {
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
// clang-format off
@@ -36,14 +48,19 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
};
// clang-format on
+ u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer();
RegisterHandlers(functions);
+ shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory));
+
+ npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
}
+IRS::~IRS() = default;
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -54,7 +71,7 @@ void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -75,7 +92,7 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -88,17 +105,23 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Stop Image processor
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedMomentProcessorConfig processor_config;
+ Core::IrSensor::PackedMomentProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
@@ -109,19 +132,28 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedClusteringProcessorConfig processor_config;
+ Core::IrSensor::PackedClusteringProcessorConfig processor_config;
};
- static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size.");
+ static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
@@ -130,17 +162,27 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessor<ClusteringProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ClusteringProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedImageTransferProcessorConfig processor_config;
+ Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
u32 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
@@ -151,20 +193,42 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
auto t_mem =
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
- "applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.transfer_memory_size, parameters.applet_resource_user_id);
+ if (t_mem.IsNull()) {
+ LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
+
+ u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
+
+ LOG_INFO(Service_IRS,
+ "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -172,32 +236,68 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.applet_resource_user_id);
+ LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+
+ if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(InvalidProcessorState);
+ return;
+ }
- IPC::ResponseBuilder rb{ctx, 5};
+ std::vector<u8> data{};
+ const auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ const auto& state = image_transfer_processor.GetState(data);
+
+ ctx.WriteBuffer(data);
+ IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
- rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks());
- rb.PushRaw<u32>(0);
+ rb.PushRaw(state);
}
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ Core::IrSensor::PackedTeraPluginProcessorConfig processor_config;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
- "applet_resource_user_id={}",
- camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
- processor_config.required_mcu_version.major,
- processor_config.required_mcu_version.minor, applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major,
+ parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
@@ -207,17 +307,17 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
npad_id != Core::HID::NpadIdType::Handheld) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(InvalidNpadId);
+ rb.Push(Service::HID::InvalidNpadId);
return;
}
- IrCameraHandle camera_handle{
+ Core::IrSensor::IrCameraHandle camera_handle{
.npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
.npad_type = Core::HID::NpadStyleIndex::None,
};
- LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}",
- npad_id, camera_handle.npad_id, camera_handle.npad_type);
+ LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
+ camera_handle.npad_id, camera_handle.npad_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -226,8 +326,8 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(
@@ -236,14 +336,23 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
+ MakeProcessor<PointingProcessor>(camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
+ image_transfer_processor.SetConfig(processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -256,14 +365,20 @@ void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Suspend image processor
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto mcu_version{rp.PopRaw<PackedMcuVersion>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(
@@ -272,37 +387,45 @@ void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
mcu_version.minor);
+ auto result = IsIrCameraHandleValid(camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Check firmware version
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- struct Parameters {
- IrCameraHandle camera_handle;
- PackedFunctionLevel function_level;
- u64 applet_resource_user_id;
- };
- static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
-
- const auto parameters{rp.PopRaw<Parameters>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.applet_resource_user_id);
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
+ camera_handle.npad_type, camera_handle.npad_id, function_level.function_level,
+ applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Set Function level
+ result = ResultSuccess;
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- PackedImageTransferProcessorExConfig processor_config;
+ Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
u64 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
@@ -313,20 +436,33 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
auto t_mem =
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
- LOG_WARNING(Service_IRS,
- "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
- "applet_resource_user_id={}",
- parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
- parameters.transfer_memory_size, parameters.applet_resource_user_id);
+ u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
+
+ LOG_INFO(Service_IRS,
+ "called, npad_type={}, npad_id={}, transfer_memory_size={}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.transfer_memory_size, parameters.applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
- const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS,
@@ -336,14 +472,23 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
+ MakeProcessor<IrLedProcessor>(camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
+ image_transfer_processor.SetConfig(processor_config);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- IrCameraHandle camera_handle;
+ Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -356,14 +501,20 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
parameters.applet_resource_user_id);
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Stop image processor async
+ result = ResultSuccess;
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
- PackedFunctionLevel function_level;
+ Core::IrSensor::PackedFunctionLevel function_level;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -378,7 +529,22 @@ void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
-IRS::~IRS() = default;
+Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
+ if (camera_handle.npad_id >
+ static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
+ return InvalidIrCameraHandle;
+ }
+ if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
+ return InvalidIrCameraHandle;
+ }
+ return ResultSuccess;
+}
+
+Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry(
+ const Core::IrSensor::IrCameraHandle& camera_handle) {
+ ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id");
+ return shared_memory->device[camera_handle.npad_id];
+}
IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
// clang-format off
@@ -395,4 +561,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
IRS_SYS::~IRS_SYS() = default;
-} // namespace Service::HID
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index 361dc2213..2e6115c73 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -4,13 +4,19 @@
#pragma once
#include "core/hid/hid_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
-namespace Service::HID {
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::IRS {
class IRS final : public ServiceFramework<IRS> {
public:
@@ -18,234 +24,19 @@ public:
~IRS() override;
private:
- // This is nn::irsensor::IrCameraStatus
- enum IrCameraStatus : u32 {
- Available,
- Unsupported,
- Unconnected,
- };
-
- // This is nn::irsensor::IrCameraInternalStatus
- enum IrCameraInternalStatus : u32 {
- Stopped,
- FirmwareUpdateNeeded,
- Unkown2,
- Unkown3,
- Unkown4,
- FirmwareVersionRequested,
- FirmwareVersionIsInvalid,
- Ready,
- Setting,
- };
-
- // This is nn::irsensor::detail::StatusManager::IrSensorMode
- enum IrSensorMode : u64 {
- None,
- MomentProcessor,
- ClusteringProcessor,
- ImageTransferProcessor,
- PointingProcessorMarker,
- TeraPluginProcessor,
- IrLedProcessor,
- };
-
- // This is nn::irsensor::ImageProcessorStatus
- enum ImageProcessorStatus : u8 {
- stopped,
- running,
- };
-
- // This is nn::irsensor::ImageTransferProcessorFormat
- enum ImageTransferProcessorFormat : u8 {
- Size320x240,
- Size160x120,
- Size80x60,
- Size40x30,
- Size20x15,
- };
-
- // This is nn::irsensor::AdaptiveClusteringMode
- enum AdaptiveClusteringMode : u8 {
- StaticFov,
- DynamicFov,
- };
-
- // This is nn::irsensor::AdaptiveClusteringTargetDistance
- enum AdaptiveClusteringTargetDistance : u8 {
- Near,
- Middle,
- Far,
- };
-
- // This is nn::irsensor::IrsHandAnalysisMode
- enum IrsHandAnalysisMode : u8 {
- Silhouette,
- Image,
- SilhoueteAndImage,
- SilhuetteOnly,
- };
-
- // This is nn::irsensor::IrSensorFunctionLevel
- enum IrSensorFunctionLevel : u8 {
- unknown0,
- unknown1,
- unknown2,
- unknown3,
- unknown4,
- };
-
- // This is nn::irsensor::IrCameraHandle
- struct IrCameraHandle {
- u8 npad_id{};
- Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
- INSERT_PADDING_BYTES(2);
- };
- static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
-
- struct IrsRect {
- s16 x;
- s16 y;
- s16 width;
- s16 height;
+ // This is nn::irsensor::detail::AruidFormat
+ struct AruidFormat {
+ u64 sensor_aruid;
+ u64 sensor_aruid_status;
};
+ static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size");
- // This is nn::irsensor::PackedMcuVersion
- struct PackedMcuVersion {
- u16 major;
- u16 minor;
+ // This is nn::irsensor::detail::StatusManager
+ struct StatusManager {
+ std::array<Core::IrSensor::DeviceFormat, 9> device;
+ std::array<AruidFormat, 5> aruid;
};
- static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
-
- // This is nn::irsensor::MomentProcessorConfig
- struct MomentProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(7);
- IrsRect window_of_interest;
- u8 preprocess;
- u8 preprocess_intensity_threshold;
- INSERT_PADDING_BYTES(5);
- };
- static_assert(sizeof(MomentProcessorConfig) == 0x28,
- "MomentProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedMomentProcessorConfig
- struct PackedMomentProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- IrsRect window_of_interest;
- PackedMcuVersion required_mcu_version;
- u8 preprocess;
- u8 preprocess_intensity_threshold;
- INSERT_PADDING_BYTES(2);
- };
- static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
- "PackedMomentProcessorConfig is an invalid size");
-
- // This is nn::irsensor::ClusteringProcessorConfig
- struct ClusteringProcessorConfig {
- u64 exposire_time;
- u32 light_target;
- u32 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(7);
- IrsRect window_of_interest;
- u32 pixel_count_min;
- u32 pixel_count_max;
- u32 object_intensity_min;
- u8 is_external_light_filter_enabled;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
- "ClusteringProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedClusteringProcessorConfig
- struct PackedClusteringProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- IrsRect window_of_interest;
- PackedMcuVersion required_mcu_version;
- u32 pixel_count_min;
- u32 pixel_count_max;
- u32 object_intensity_min;
- u8 is_external_light_filter_enabled;
- INSERT_PADDING_BYTES(2);
- };
- static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
- "PackedClusteringProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedImageTransferProcessorConfig
- struct PackedImageTransferProcessorConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- PackedMcuVersion required_mcu_version;
- u8 format;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
- "PackedImageTransferProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedTeraPluginProcessorConfig
- struct PackedTeraPluginProcessorConfig {
- PackedMcuVersion required_mcu_version;
- u8 mode;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
- "PackedTeraPluginProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedPointingProcessorConfig
- struct PackedPointingProcessorConfig {
- IrsRect window_of_interest;
- PackedMcuVersion required_mcu_version;
- };
- static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
- "PackedPointingProcessorConfig is an invalid size");
-
- // This is nn::irsensor::PackedFunctionLevel
- struct PackedFunctionLevel {
- IrSensorFunctionLevel function_level;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
-
- // This is nn::irsensor::PackedImageTransferProcessorExConfig
- struct PackedImageTransferProcessorExConfig {
- u64 exposire_time;
- u8 light_target;
- u8 gain;
- u8 is_negative_used;
- INSERT_PADDING_BYTES(5);
- PackedMcuVersion required_mcu_version;
- ImageTransferProcessorFormat origin_format;
- ImageTransferProcessorFormat trimming_format;
- u16 trimming_start_x;
- u16 trimming_start_y;
- u8 is_external_light_filter_enabled;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
- "PackedImageTransferProcessorExConfig is an invalid size");
-
- // This is nn::irsensor::PackedIrLedProcessorConfig
- struct PackedIrLedProcessorConfig {
- PackedMcuVersion required_mcu_version;
- u8 light_target;
- INSERT_PADDING_BYTES(3);
- };
- static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
- "PackedIrLedProcessorConfig is an invalid size");
+ static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
@@ -265,6 +56,56 @@ private:
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
+
+ Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
+ Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
+ const Core::IrSensor::IrCameraHandle& camera_handle);
+
+ template <typename T>
+ void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle,
+ Core::IrSensor::DeviceFormat& device_state) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return;
+ }
+ processors[index] = std::make_unique<T>(device_state);
+ }
+
+ template <typename T>
+ void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle,
+ Core::IrSensor::DeviceFormat& device_state) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return;
+ }
+ processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index);
+ }
+
+ template <typename T>
+ T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return static_cast<T&>(*processors[0]);
+ }
+ return static_cast<T&>(*processors[index]);
+ }
+
+ template <typename T>
+ const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return static_cast<T&>(*processors[0]);
+ }
+ return static_cast<T&>(*processors[index]);
+ }
+
+ Core::HID::EmulatedController* npad_device = nullptr;
+ StatusManager* shared_memory = nullptr;
+ std::array<std::unique_ptr<ProcessorBase>, 9> processors{};
};
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
@@ -273,4 +114,4 @@ public:
~IRS_SYS() override;
};
-} // namespace Service::HID
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
new file mode 100644
index 000000000..6479af212
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/clustering_processor.h"
+
+namespace Service::IRS {
+ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+ClusteringProcessor::~ClusteringProcessor() = default;
+
+void ClusteringProcessor::StartProcessor() {}
+
+void ClusteringProcessor::SuspendProcessor() {}
+
+void ClusteringProcessor::StopProcessor() {}
+
+void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.pixel_count_min = config.pixel_count_min;
+ current_config.pixel_count_max = config.pixel_count_max;
+ current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
+ current_config.object_intensity_min = config.object_intensity_min;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h
new file mode 100644
index 000000000..6e2ba8846
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.h
@@ -0,0 +1,74 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class ClusteringProcessor final : public ProcessorBase {
+public:
+ explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~ClusteringProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
+
+private:
+ // This is nn::irsensor::ClusteringProcessorConfig
+ struct ClusteringProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::IrsRect window_of_interest;
+ u32 pixel_count_min;
+ u32 pixel_count_max;
+ u32 object_intensity_min;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
+ "ClusteringProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::AdaptiveClusteringProcessorConfig
+ struct AdaptiveClusteringProcessorConfig {
+ Core::IrSensor::AdaptiveClusteringMode mode;
+ Core::IrSensor::AdaptiveClusteringTargetDistance target_distance;
+ };
+ static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8,
+ "AdaptiveClusteringProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::ClusteringData
+ struct ClusteringData {
+ f32 average_intensity;
+ Core::IrSensor::IrsCentroid centroid;
+ u32 pixel_count;
+ Core::IrSensor::IrsRect bound;
+ };
+ static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size");
+
+ // This is nn::irsensor::ClusteringProcessorState
+ struct ClusteringProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ u8 object_count;
+ INSERT_PADDING_BYTES(3);
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ std::array<ClusteringData, 0x10> data;
+ };
+ static_assert(sizeof(ClusteringProcessorState) == 0x198,
+ "ClusteringProcessorState is an invalid size");
+
+ ClusteringProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
new file mode 100644
index 000000000..98f0c579d
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
@@ -0,0 +1,150 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
+
+namespace Service::IRS {
+ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index)
+ : device{device_format} {
+ npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
+
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
+ .is_npad_service = true,
+ };
+ callback_key = npad_device->SetCallback(engine_callback);
+
+ device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+ImageTransferProcessor::~ImageTransferProcessor() {
+ npad_device->DeleteCallback(callback_key);
+};
+
+void ImageTransferProcessor::StartProcessor() {
+ is_active = true;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Available;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
+ processor_state.sampling_number = 0;
+ processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+}
+
+void ImageTransferProcessor::SuspendProcessor() {}
+
+void ImageTransferProcessor::StopProcessor() {}
+
+void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
+ if (type != Core::HID::ControllerTriggerType::IrSensor) {
+ return;
+ }
+ if (!is_transfer_memory_set) {
+ return;
+ }
+
+ const auto camera_data = npad_device->GetCamera();
+
+ // This indicates how much ambient light is precent
+ processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+ processor_state.sampling_number = camera_data.sample;
+
+ if (camera_data.format != current_config.origin_format) {
+ LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
+ current_config.origin_format);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ if (current_config.origin_format > current_config.trimming_format) {
+ LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
+ current_config.origin_format, current_config.trimming_format);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ std::vector<u8> window_data{};
+ const auto origin_width = GetDataWidth(current_config.origin_format);
+ const auto origin_height = GetDataHeight(current_config.origin_format);
+ const auto trimming_width = GetDataWidth(current_config.trimming_format);
+ const auto trimming_height = GetDataHeight(current_config.trimming_format);
+ window_data.resize(GetDataSize(current_config.trimming_format));
+
+ if (trimming_width + current_config.trimming_start_x > origin_width ||
+ trimming_height + current_config.trimming_start_y > origin_height) {
+ LOG_WARNING(Service_IRS,
+ "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
+ current_config.trimming_start_x, current_config.trimming_start_y,
+ trimming_width, trimming_height, origin_width, origin_height);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ for (std::size_t y = 0; y < trimming_height; y++) {
+ for (std::size_t x = 0; x < trimming_width; x++) {
+ const std::size_t window_index = (y * trimming_width) + x;
+ const std::size_t origin_index =
+ ((y + current_config.trimming_start_y) * origin_width) + x +
+ current_config.trimming_start_x;
+ window_data[window_index] = camera_data.data[origin_index];
+ }
+ }
+
+ memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format));
+
+ if (!IsProcessorActive()) {
+ StartProcessor();
+ }
+}
+
+void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.origin_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
+ current_config.trimming_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
+ current_config.trimming_start_x = 0;
+ current_config.trimming_start_y = 0;
+
+ npad_device->SetCameraFormat(current_config.origin_format);
+}
+
+void ImageTransferProcessor::SetConfig(
+ Core::IrSensor::PackedImageTransferProcessorExConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.origin_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format);
+ current_config.trimming_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format);
+ current_config.trimming_start_x = config.trimming_start_x;
+ current_config.trimming_start_y = config.trimming_start_y;
+
+ npad_device->SetCameraFormat(current_config.origin_format);
+}
+
+void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) {
+ is_transfer_memory_set = true;
+ transfer_memory = t_mem;
+}
+
+Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
+ std::vector<u8>& data) const {
+ const auto size = GetDataSize(current_config.trimming_format);
+ data.resize(size);
+ memcpy(data.data(), transfer_memory, size);
+ return processor_state;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.h b/src/core/hle/service/hid/irsensor/image_transfer_processor.h
new file mode 100644
index 000000000..393df492d
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.h
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::IRS {
+class ImageTransferProcessor final : public ProcessorBase {
+public:
+ explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index);
+ ~ImageTransferProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config);
+ void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
+
+ // Transfer memory where the image data will be stored
+ void SetTransferMemoryPointer(u8* t_mem);
+
+ Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
+
+private:
+ // This is nn::irsensor::ImageTransferProcessorConfig
+ struct ImageTransferProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::ImageTransferProcessorFormat format;
+ };
+ static_assert(sizeof(ImageTransferProcessorConfig) == 0x20,
+ "ImageTransferProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::ImageTransferProcessorExConfig
+ struct ImageTransferProcessorExConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::ImageTransferProcessorFormat origin_format;
+ Core::IrSensor::ImageTransferProcessorFormat trimming_format;
+ u16 trimming_start_x;
+ u16 trimming_start_y;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28,
+ "ImageTransferProcessorExConfig is an invalid size");
+
+ void OnControllerUpdate(Core::HID::ControllerTriggerType type);
+
+ ImageTransferProcessorExConfig current_config{};
+ Core::IrSensor::ImageTransferProcessorState processor_state{};
+ Core::IrSensor::DeviceFormat& device;
+ Core::HID::EmulatedController* npad_device;
+ int callback_key{};
+
+ u8* transfer_memory = nullptr;
+ bool is_transfer_memory_set = false;
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.cpp b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp
new file mode 100644
index 000000000..8e6dd99e4
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/ir_led_processor.h"
+
+namespace Service::IRS {
+IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+IrLedProcessor::~IrLedProcessor() = default;
+
+void IrLedProcessor::StartProcessor() {}
+
+void IrLedProcessor::SuspendProcessor() {}
+
+void IrLedProcessor::StopProcessor() {}
+
+void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) {
+ current_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.light_target);
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.h b/src/core/hle/service/hid/irsensor/ir_led_processor.h
new file mode 100644
index 000000000..c3d8693c9
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/ir_led_processor.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class IrLedProcessor final : public ProcessorBase {
+public:
+ explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~IrLedProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config);
+
+private:
+ // This is nn::irsensor::IrLedProcessorConfig
+ struct IrLedProcessorConfig {
+ Core::IrSensor::CameraLightTarget light_target;
+ };
+ static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size");
+
+ struct IrLedProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ std::array<u8, 0x8> data;
+ };
+ static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size");
+
+ IrLedProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp
new file mode 100644
index 000000000..dbaca420a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/moment_processor.h"
+
+namespace Service::IRS {
+MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+MomentProcessor::~MomentProcessor() = default;
+
+void MomentProcessor::StartProcessor() {}
+
+void MomentProcessor::SuspendProcessor() {}
+
+void MomentProcessor::StopProcessor() {}
+
+void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.window_of_interest = config.window_of_interest;
+ current_config.preprocess =
+ static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
+ current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h
new file mode 100644
index 000000000..d4bd22e0f
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/moment_processor.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class MomentProcessor final : public ProcessorBase {
+public:
+ explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~MomentProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
+
+private:
+ // This is nn::irsensor::MomentProcessorConfig
+ struct MomentProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::IrsRect window_of_interest;
+ Core::IrSensor::MomentProcessorPreprocess preprocess;
+ u32 preprocess_intensity_threshold;
+ };
+ static_assert(sizeof(MomentProcessorConfig) == 0x28,
+ "MomentProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::MomentStatistic
+ struct MomentStatistic {
+ f32 average_intensity;
+ Core::IrSensor::IrsCentroid centroid;
+ };
+ static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size");
+
+ // This is nn::irsensor::MomentProcessorState
+ struct MomentProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ INSERT_PADDING_BYTES(4);
+ std::array<MomentStatistic, 0x30> stadistic;
+ };
+ static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
+
+ MomentProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.cpp b/src/core/hle/service/hid/irsensor/pointing_processor.cpp
new file mode 100644
index 000000000..929f177fc
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/pointing_processor.cpp
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/pointing_processor.h"
+
+namespace Service::IRS {
+PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+PointingProcessor::~PointingProcessor() = default;
+
+void PointingProcessor::StartProcessor() {}
+
+void PointingProcessor::SuspendProcessor() {}
+
+void PointingProcessor::StopProcessor() {}
+
+void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) {
+ current_config.window_of_interest = config.window_of_interest;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h
new file mode 100644
index 000000000..cf4930794
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/pointing_processor.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class PointingProcessor final : public ProcessorBase {
+public:
+ explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~PointingProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config);
+
+private:
+ // This is nn::irsensor::PointingProcessorConfig
+ struct PointingProcessorConfig {
+ Core::IrSensor::IrsRect window_of_interest;
+ };
+ static_assert(sizeof(PointingProcessorConfig) == 0x8,
+ "PointingProcessorConfig is an invalid size");
+
+ struct PointingProcessorMarkerData {
+ u8 pointing_status;
+ INSERT_PADDING_BYTES(3);
+ u32 unknown;
+ float unkown_float1;
+ float position_x;
+ float position_y;
+ float unkown_float2;
+ Core::IrSensor::IrsRect window_of_interest;
+ };
+ static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
+ "PointingProcessorMarkerData is an invalid size");
+
+ struct PointingProcessorMarkerState {
+ s64 sampling_number;
+ u64 timestamp;
+ std::array<PointingProcessorMarkerData, 0x3> data;
+ };
+ static_assert(sizeof(PointingProcessorMarkerState) == 0x70,
+ "PointingProcessorMarkerState is an invalid size");
+
+ PointingProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/processor_base.cpp b/src/core/hle/service/hid/irsensor/processor_base.cpp
new file mode 100644
index 000000000..4d43ca17a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/processor_base.cpp
@@ -0,0 +1,67 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+
+ProcessorBase::ProcessorBase() {}
+ProcessorBase::~ProcessorBase() = default;
+
+bool ProcessorBase::IsProcessorActive() const {
+ return is_active;
+}
+
+std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 320 * 240;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 160 * 120;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 80 * 60;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 40 * 30;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 20 * 15;
+ default:
+ return 0;
+ }
+}
+
+std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 320;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 160;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 80;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 40;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 20;
+ default:
+ return 0;
+ }
+}
+
+std::size_t ProcessorBase::GetDataHeight(
+ Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 240;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 120;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 60;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 30;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 15;
+ default:
+ return 0;
+ }
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/processor_base.h b/src/core/hle/service/hid/irsensor/processor_base.h
new file mode 100644
index 000000000..bc0d2977b
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/processor_base.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+
+namespace Service::IRS {
+class ProcessorBase {
+public:
+ explicit ProcessorBase();
+ virtual ~ProcessorBase();
+
+ virtual void StartProcessor() = 0;
+ virtual void SuspendProcessor() = 0;
+ virtual void StopProcessor() = 0;
+
+ bool IsProcessorActive() const;
+
+protected:
+ /// Returns the number of bytes the image uses
+ std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ /// Returns the width of the image
+ std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ /// Returns the height of the image
+ std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ bool is_active{false};
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
new file mode 100644
index 000000000..e691c840a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
+
+namespace Service::IRS {
+TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+TeraPluginProcessor::~TeraPluginProcessor() = default;
+
+void TeraPluginProcessor::StartProcessor() {}
+
+void TeraPluginProcessor::SuspendProcessor() {}
+
+void TeraPluginProcessor::StopProcessor() {}
+
+void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) {
+ current_config.mode = config.mode;
+ current_config.unknown_1 = config.unknown_1;
+ current_config.unknown_2 = config.unknown_2;
+ current_config.unknown_3 = config.unknown_3;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.h b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h
new file mode 100644
index 000000000..bbea7ed0b
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class TeraPluginProcessor final : public ProcessorBase {
+public:
+ explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~TeraPluginProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config);
+
+private:
+ // This is nn::irsensor::TeraPluginProcessorConfig
+ struct TeraPluginProcessorConfig {
+ u8 mode;
+ u8 unknown_1;
+ u8 unknown_2;
+ u8 unknown_3;
+ };
+ static_assert(sizeof(TeraPluginProcessorConfig) == 0x4,
+ "TeraPluginProcessorConfig is an invalid size");
+
+ struct TeraPluginProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ std::array<u8, 0x12c> data;
+ };
+ static_assert(sizeof(TeraPluginProcessorState) == 0x140,
+ "TeraPluginProcessorState is an invalid size");
+
+ TeraPluginProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 0310ce883..7055ea93e 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -30,6 +30,19 @@ enum class RequestState : u32 {
Connected = 3,
};
+enum class InternetConnectionType : u8 {
+ WiFi = 1,
+ Ethernet = 2,
+};
+
+enum class InternetConnectionStatus : u8 {
+ ConnectingUnknown1,
+ ConnectingUnknown2,
+ ConnectingUnknown3,
+ ConnectingUnknown4,
+ Connected,
+};
+
struct IpAddressSetting {
bool is_automatic{};
Network::IPv4Address current_address{};
@@ -271,6 +284,7 @@ private:
rb.Push(ResultSuccess);
rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
}
+
void CreateScanRequest(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
@@ -279,6 +293,7 @@ private:
rb.Push(ResultSuccess);
rb.PushIpcInterface<IScanRequest>(system);
}
+
void CreateRequest(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
@@ -287,6 +302,7 @@ private:
rb.Push(ResultSuccess);
rb.PushIpcInterface<IRequest>(system);
}
+
void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -335,12 +351,14 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
+
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
+
void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -354,6 +372,7 @@ private:
rb.Push(ResultSuccess);
rb.PushRaw(*ipv4);
}
+
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
@@ -369,6 +388,7 @@ private:
rb.PushIpcInterface<INetworkProfile>(system);
rb.PushRaw<u128>(uuid);
}
+
void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -405,6 +425,7 @@ private:
rb.Push(ResultSuccess);
rb.PushRaw<IpConfigInfo>(ip_config_info);
}
+
void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -412,6 +433,24 @@ private:
rb.Push(ResultSuccess);
rb.Push<u8>(0);
}
+
+ void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ struct Output {
+ InternetConnectionType type{InternetConnectionType::WiFi};
+ u8 wifi_strength{3};
+ InternetConnectionStatus state{InternetConnectionStatus::Connected};
+ };
+ static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
+
+ constexpr Output out{};
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(out);
+ }
+
void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -423,6 +462,7 @@ private:
rb.Push<u8>(0);
}
}
+
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -456,7 +496,7 @@ IGeneralService::IGeneralService(Core::System& system_)
{15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"},
{16, nullptr, "SetWirelessCommunicationEnabled"},
{17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
- {18, nullptr, "GetInternetConnectionStatus"},
+ {18, &IGeneralService::GetInternetConnectionStatus, "GetInternetConnectionStatus"},
{19, nullptr, "SetEthernetCommunicationEnabled"},
{20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"},
{21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"},
diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h
index 21aaa40cd..157333ff8 100644
--- a/src/core/hle/service/nvflinger/binder.h
+++ b/src/core/hle/service/nvflinger/binder.h
@@ -34,6 +34,7 @@ enum class TransactionId {
class IBinder {
public:
+ virtual ~IBinder() = default;
virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
u32 flags) = 0;
virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 5f69c8c2c..5574269eb 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -287,9 +287,21 @@ s64 NVFlinger::GetNextTicks() const {
static constexpr s64 max_hertz = 120LL;
const auto& settings = Settings::values;
- const bool unlocked_fps = settings.disable_fps_limit.GetValue();
- const s64 fps_cap = unlocked_fps ? static_cast<s64>(settings.fps_cap.GetValue()) : 1;
- return (1000000000 * (1LL << swap_interval)) / (max_hertz * fps_cap);
+ auto speed_scale = 1.f;
+ if (settings.use_multi_core.GetValue()) {
+ if (settings.use_speed_limit.GetValue()) {
+ // Scales the speed based on speed_limit setting on MC. SC is handled by
+ // SpeedLimiter::DoSpeedLimiting.
+ speed_scale = 100.f / settings.speed_limit.GetValue();
+ } else {
+ // Run at unlocked framerate.
+ speed_scale = 0.01f;
+ }
+ }
+
+ const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz);
+
+ return static_cast<s64>(speed_scale * static_cast<float>(next_ticks));
}
} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
index 9e0eb6ac0..2c31e9485 100644
--- a/src/core/hle/service/ptm/psm.cpp
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -9,10 +9,8 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/ptm/psm.h"
-#include "core/hle/service/service.h"
-#include "core/hle/service/sm/sm.h"
-namespace Service::PSM {
+namespace Service::PTM {
class IPsmSession final : public ServiceFramework<IPsmSession> {
public:
@@ -57,7 +55,7 @@ public:
private:
void BindStateChangeEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ LOG_DEBUG(Service_PTM, "called");
should_signal = true;
@@ -67,7 +65,7 @@ private:
}
void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ LOG_DEBUG(Service_PTM, "called");
should_signal = false;
@@ -78,7 +76,7 @@ private:
void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_charger_type = state;
@@ -89,7 +87,7 @@ private:
void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_power_supply = state;
@@ -100,7 +98,7 @@ private:
void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_battery_voltage = state;
@@ -117,76 +115,58 @@ private:
Kernel::KEvent* state_change_event;
};
-class PSM final : public ServiceFramework<PSM> {
-public:
- explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
- {1, &PSM::GetChargerType, "GetChargerType"},
- {2, nullptr, "EnableBatteryCharging"},
- {3, nullptr, "DisableBatteryCharging"},
- {4, nullptr, "IsBatteryChargingEnabled"},
- {5, nullptr, "AcquireControllerPowerSupply"},
- {6, nullptr, "ReleaseControllerPowerSupply"},
- {7, &PSM::OpenSession, "OpenSession"},
- {8, nullptr, "EnableEnoughPowerChargeEmulation"},
- {9, nullptr, "DisableEnoughPowerChargeEmulation"},
- {10, nullptr, "EnableFastBatteryCharging"},
- {11, nullptr, "DisableFastBatteryCharging"},
- {12, nullptr, "GetBatteryVoltageState"},
- {13, nullptr, "GetRawBatteryChargePercentage"},
- {14, nullptr, "IsEnoughPowerSupplied"},
- {15, nullptr, "GetBatteryAgePercentage"},
- {16, nullptr, "GetBatteryChargeInfoEvent"},
- {17, nullptr, "GetBatteryChargeInfoFields"},
- {18, nullptr, "GetBatteryChargeCalibratedEvent"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
- ~PSM() override = default;
-
-private:
- void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+PSM::PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
+ {1, &PSM::GetChargerType, "GetChargerType"},
+ {2, nullptr, "EnableBatteryCharging"},
+ {3, nullptr, "DisableBatteryCharging"},
+ {4, nullptr, "IsBatteryChargingEnabled"},
+ {5, nullptr, "AcquireControllerPowerSupply"},
+ {6, nullptr, "ReleaseControllerPowerSupply"},
+ {7, &PSM::OpenSession, "OpenSession"},
+ {8, nullptr, "EnableEnoughPowerChargeEmulation"},
+ {9, nullptr, "DisableEnoughPowerChargeEmulation"},
+ {10, nullptr, "EnableFastBatteryCharging"},
+ {11, nullptr, "DisableFastBatteryCharging"},
+ {12, nullptr, "GetBatteryVoltageState"},
+ {13, nullptr, "GetRawBatteryChargePercentage"},
+ {14, nullptr, "IsEnoughPowerSupplied"},
+ {15, nullptr, "GetBatteryAgePercentage"},
+ {16, nullptr, "GetBatteryChargeInfoEvent"},
+ {17, nullptr, "GetBatteryChargeInfoFields"},
+ {18, nullptr, "GetBatteryChargeCalibratedEvent"},
+ };
+ // clang-format on
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(battery_charge_percentage);
- }
+ RegisterHandlers(functions);
+}
- void GetChargerType(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+PSM::~PSM() = default;
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(charger_type);
- }
+void PSM::GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
- void OpenSession(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(battery_charge_percentage);
+}
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IPsmSession>(system);
- }
+void PSM::GetChargerType(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
- enum class ChargerType : u32 {
- Unplugged = 0,
- RegularCharger = 1,
- LowPowerCharger = 2,
- Unknown = 3,
- };
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(charger_type);
+}
- u32 battery_charge_percentage{100}; // 100%
- ChargerType charger_type{ChargerType::RegularCharger};
-};
+void PSM::OpenSession(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
-void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
- std::make_shared<PSM>(system)->InstallAsService(sm);
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IPsmSession>(system);
}
-} // namespace Service::PSM
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h
index 94a1044db..f674ba8bc 100644
--- a/src/core/hle/service/ptm/psm.h
+++ b/src/core/hle/service/ptm/psm.h
@@ -3,16 +3,29 @@
#pragma once
-namespace Core {
-class System;
-}
+#include "core/hle/service/service.h"
-namespace Service::SM {
-class ServiceManager;
-}
+namespace Service::PTM {
-namespace Service::PSM {
+class PSM final : public ServiceFramework<PSM> {
+public:
+ explicit PSM(Core::System& system_);
+ ~PSM() override;
-void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
+private:
+ enum class ChargerType : u32 {
+ Unplugged = 0,
+ RegularCharger = 1,
+ LowPowerCharger = 2,
+ Unknown = 3,
+ };
-} // namespace Service::PSM
+ void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx);
+ void GetChargerType(Kernel::HLERequestContext& ctx);
+ void OpenSession(Kernel::HLERequestContext& ctx);
+
+ u32 battery_charge_percentage{100};
+ ChargerType charger_type{ChargerType::RegularCharger};
+};
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
new file mode 100644
index 000000000..4bea995c6
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+
+#include "core/core.h"
+#include "core/hle/service/ptm/psm.h"
+#include "core/hle/service/ptm/ptm.h"
+#include "core/hle/service/ptm/ts.h"
+
+namespace Service::PTM {
+
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
+ std::make_shared<PSM>(system)->InstallAsService(sm);
+ std::make_shared<TS>(system)->InstallAsService(sm);
+}
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
new file mode 100644
index 000000000..06224a24e
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.h
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+namespace Core {
+class System;
+}
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::PTM {
+
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp
new file mode 100644
index 000000000..65c3f135f
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.cpp
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+
+#include "core/core.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/ptm/ts.h"
+
+namespace Service::PTM {
+
+TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetTemperatureRange"},
+ {1, &TS::GetTemperature, "GetTemperature"},
+ {2, nullptr, "SetMeasurementMode"},
+ {3, nullptr, "GetTemperatureMilliC"},
+ {4, nullptr, "OpenSession"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+TS::~TS() = default;
+
+void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto location{rp.PopEnum<Location>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location);
+
+ const s32 temperature = location == Location::Internal ? 35 : 20;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(temperature);
+}
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h
new file mode 100644
index 000000000..39a734ef7
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::PTM {
+
+class TS final : public ServiceFramework<TS> {
+public:
+ explicit TS(Core::System& system_);
+ ~TS() override;
+
+private:
+ enum class Location : u8 {
+ Internal,
+ External,
+ };
+
+ void GetTemperature(Kernel::HLERequestContext& ctx);
+};
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 318009e4f..c64291e7f 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -58,7 +58,7 @@
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/prepo/prepo.h"
#include "core/hle/service/psc/psc.h"
-#include "core/hle/service/ptm/psm.h"
+#include "core/hle/service/ptm/ptm.h"
#include "core/hle/service/service.h"
#include "core/hle/service/set/settings.h"
#include "core/hle/service/sm/sm.h"
@@ -287,7 +287,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
PlayReport::InstallInterfaces(*sm, system);
PM::InstallInterfaces(system);
PSC::InstallInterfaces(*sm, system);
- PSM::InstallInterfaces(*sm, system);
+ PTM::InstallInterfaces(*sm, system);
Set::InstallInterfaces(*sm, system);
Sockets::InstallInterfaces(*sm, system);
SPL::InstallInterfaces(*sm, system);
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 5114b8be2..3e9dc4a13 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -720,7 +720,25 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message)
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
- return Translate(file_descriptors[fd]->socket->Recv(flags, message));
+
+ FileDescriptor& descriptor = *file_descriptors[fd];
+
+ // Apply flags
+ if ((flags & FLAG_MSG_DONTWAIT) != 0) {
+ flags &= ~FLAG_MSG_DONTWAIT;
+ if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
+ descriptor.socket->SetNonBlock(true);
+ }
+ }
+
+ const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
+
+ // Restore original state
+ if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
+ descriptor.socket->SetNonBlock(false);
+ }
+
+ return {ret, bsd_errno};
}
std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,