diff options
Diffstat (limited to 'src/core')
25 files changed, 630 insertions, 156 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4755ec822..64fdf38cd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -156,6 +156,8 @@ add_library(core STATIC hle/service/am/omm.h hle/service/am/spsm.cpp hle/service/am/spsm.h + hle/service/am/tcap.cpp + hle/service/am/tcap.h hle/service/aoc/aoc_u.cpp hle/service/aoc/aoc_u.h hle/service/apm/apm.cpp @@ -280,6 +282,8 @@ add_library(core STATIC hle/service/nifm/nifm.h hle/service/nim/nim.cpp hle/service/nim/nim.h + hle/service/npns/npns.cpp + hle/service/npns/npns.h hle/service/ns/ns.cpp hle/service/ns/ns.h hle/service/ns/pl_u.cpp @@ -327,6 +331,8 @@ add_library(core STATIC hle/service/prepo/prepo.h hle/service/psc/psc.cpp hle/service/psc/psc.h + hle/service/ptm/psm.cpp + hle/service/ptm/psm.h hle/service/service.cpp hle/service/service.h hle/service/set/set.cpp diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index edad5f1b1..68d5376cb 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -77,7 +77,8 @@ HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_ses HLERequestContext::~HLERequestContext() = default; -void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { +void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, + bool incoming) { IPC::RequestParser rp(src_cmdbuf); command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); @@ -94,8 +95,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { rp.Skip(2, false); } if (incoming) { - auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); - // Populate the object lists with the data in the IPC request. for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>())); @@ -189,10 +188,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. } -ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, - Process& src_process, - HandleTable& src_table) { - ParseCommandBuffer(src_cmdbuf, true); +ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, + u32_le* src_cmdbuf) { + ParseCommandBuffer(handle_table, src_cmdbuf, true); if (command_header->type == IPC::CommandType::Close) { // Close does not populate the rest of the IPC header return RESULT_SUCCESS; @@ -207,14 +205,17 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb return RESULT_SUCCESS; } -ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) { +ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { + auto& owner_process = *thread.GetOwnerProcess(); + auto& handle_table = owner_process.GetHandleTable(); + std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; - Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), + Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), dst_cmdbuf.size() * sizeof(u32)); // The header was already built in the internal command buffer. Attempt to parse it to verify // the integrity and then copy it over to the target command buffer. - ParseCommandBuffer(cmd_buf.data(), false); + ParseCommandBuffer(handle_table, cmd_buf.data(), false); // The data_size already includes the payload header, the padding and the domain header. std::size_t size = data_payload_offset + command_header->data_size - @@ -236,8 +237,6 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy); ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move); - auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); - // We don't make a distinction between copy and move handles when translating since HLE // services don't deal with handles directly. However, the guest applications might check // for specific values in each of these descriptors. @@ -268,7 +267,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) } // Copy the translated command buffer back into the thread's command buffer area. - Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), + Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), dst_cmdbuf.size() * sizeof(u32)); return RESULT_SUCCESS; diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 894479ee0..f01491daa 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -24,10 +24,10 @@ class ServiceFrameworkBase; namespace Kernel { class Domain; +class Event; class HandleTable; class HLERequestContext; class Process; -class Event; /** * Interface implemented by HLE Session handlers. @@ -126,13 +126,12 @@ public: u64 timeout, WakeupCallback&& callback, Kernel::SharedPtr<Kernel::Event> event = nullptr); - void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming); - /// Populates this context with data from the requesting process/thread. - ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, - HandleTable& src_table); + ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, + u32_le* src_cmdbuf); + /// Writes data from this context back to the requesting process/thread. - ResultCode WriteToOutgoingCommandBuffer(const Thread& thread); + ResultCode WriteToOutgoingCommandBuffer(Thread& thread); u32_le GetCommand() const { return command; @@ -255,6 +254,8 @@ public: std::string Description() const; private: + void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming); + std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; SharedPtr<Kernel::ServerSession> server_session; // TODO(yuriks): Check common usage of this and optimize size accordingly diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index bd680adfe..4b6b32dd5 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -118,7 +118,6 @@ struct KernelCore::Impl { process_list.clear(); current_process = nullptr; - handle_table.Clear(); resource_limits.fill(nullptr); thread_wakeup_callback_handle_table.Clear(); @@ -209,7 +208,6 @@ struct KernelCore::Impl { std::vector<SharedPtr<Process>> process_list; Process* current_process = nullptr; - Kernel::HandleTable handle_table; std::array<SharedPtr<ResourceLimit>, 4> resource_limits; /// The event type of the generic timer callback event @@ -241,14 +239,6 @@ void KernelCore::Shutdown() { impl->Shutdown(); } -Kernel::HandleTable& KernelCore::HandleTable() { - return impl->handle_table; -} - -const Kernel::HandleTable& KernelCore::HandleTable() const { - return impl->handle_table; -} - SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory( ResourceLimitCategory category) const { return impl->resource_limits.at(static_cast<std::size_t>(category)); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 41554821f..7f822d524 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -47,12 +47,6 @@ public: /// Clears all resources in use by the kernel instance. void Shutdown(); - /// Provides a reference to the handle table. - Kernel::HandleTable& HandleTable(); - - /// Provides a const reference to the handle table. - const Kernel::HandleTable& HandleTable() const; - /// Retrieves a shared pointer to a ResourceLimit identified by the given category. SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const; diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index f2816943a..148478488 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -13,6 +13,7 @@ #include <boost/container/static_vector.hpp> #include "common/bit_field.h" #include "common/common_types.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" @@ -142,6 +143,16 @@ public: return vm_manager; } + /// Gets a reference to the process' handle table. + HandleTable& GetHandleTable() { + return handle_table; + } + + /// Gets a const reference to the process' handle table. + const HandleTable& GetHandleTable() const { + return handle_table; + } + /// Gets the current status of the process ProcessStatus GetStatus() const { return status; @@ -294,6 +305,9 @@ private: /// specified by metadata provided to the process during loading. bool is_64bit_process = true; + /// Per-process handle table for storing created object handles in. + HandleTable handle_table; + std::string name; }; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 1ece691c7..5fc320403 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -107,8 +107,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { // similar. Kernel::HLERequestContext context(this); u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); - context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(), - kernel.HandleTable()); + context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); ResultCode result = RESULT_SUCCESS; // If the session has been converted to a domain, handle the domain request diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 3b8a2e230..67ea67666 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -189,14 +189,15 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address CASCADE_RESULT(client_session, client_port->Connect()); // Return the client session - CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session)); + auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + CASCADE_RESULT(*out_handle, handle_table.Create(client_session)); return RESULT_SUCCESS; } /// Makes a blocking IPC call to an OS service. static ResultCode SendSyncRequest(Handle handle) { - auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<ClientSession> session = kernel.HandleTable().Get<ClientSession>(handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle); if (!session) { LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); return ERR_INVALID_HANDLE; @@ -215,8 +216,8 @@ static ResultCode SendSyncRequest(Handle handle) { static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { return ERR_INVALID_HANDLE; } @@ -229,8 +230,8 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { static ResultCode GetProcessId(u32* process_id, Handle process_handle) { LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Process> process = handle_table.Get<Process>(process_handle); if (!process) { return ERR_INVALID_HANDLE; } @@ -273,11 +274,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 using ObjectPtr = Thread::ThreadWaitObjects::value_type; Thread::ThreadWaitObjects objects(handle_count); - auto& kernel = Core::System::GetInstance().Kernel(); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); for (u64 i = 0; i < handle_count; ++i) { const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); - const auto object = kernel.HandleTable().Get<WaitObject>(handle); + const auto object = handle_table.Get<WaitObject>(handle); if (object == nullptr) { return ERR_INVALID_HANDLE; @@ -325,8 +326,8 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 static ResultCode CancelSynchronization(Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { return ERR_INVALID_HANDLE; } @@ -354,7 +355,7 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, return ERR_INVALID_ADDRESS; } - auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); + auto& handle_table = Core::CurrentProcess()->GetHandleTable(); return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, requesting_thread_handle); } @@ -499,13 +500,12 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) { static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); + const auto* current_process = Core::CurrentProcess(); + const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); if (!thread) { return ERR_INVALID_HANDLE; } - const auto* current_process = Core::CurrentProcess(); if (thread->GetOwnerProcess() != current_process) { return ERR_INVALID_HANDLE; } @@ -531,10 +531,11 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { /// Gets the priority for the specified thread static ResultCode GetThreadPriority(u32* priority, Handle handle) { - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); - if (!thread) + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle); + if (!thread) { return ERR_INVALID_HANDLE; + } *priority = thread->GetPriority(); return RESULT_SUCCESS; @@ -546,14 +547,15 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { return ERR_INVALID_THREAD_PRIORITY; } - auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); - if (!thread) + const auto* const current_process = Core::CurrentProcess(); + SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); + if (!thread) { return ERR_INVALID_HANDLE; + } // Note: The kernel uses the current process's resource limit instead of // the one from the thread owner's resource limit. - const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); + const ResourceLimit& resource_limit = current_process->GetResourceLimit(); if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } @@ -595,15 +597,13 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s return ERR_INVALID_MEMORY_PERMISSIONS; } - auto& kernel = Core::System::GetInstance().Kernel(); - auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); + auto* const current_process = Core::CurrentProcess(); + auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); if (!shared_memory) { return ERR_INVALID_HANDLE; } - auto* const current_process = Core::CurrentProcess(); const auto& vm_manager = current_process->VMManager(); - if (!vm_manager.IsWithinASLRRegion(addr, size)) { return ERR_INVALID_MEMORY_RANGE; } @@ -627,15 +627,13 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 return ERR_INVALID_ADDRESS_STATE; } - auto& kernel = Core::System::GetInstance().Kernel(); - auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); + auto* const current_process = Core::CurrentProcess(); + auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); if (!shared_memory) { return ERR_INVALID_HANDLE; } - auto* const current_process = Core::CurrentProcess(); const auto& vm_manager = current_process->VMManager(); - if (!vm_manager.IsWithinASLRRegion(addr, size)) { return ERR_INVALID_MEMORY_RANGE; } @@ -646,15 +644,14 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 /// Query process memory static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, Handle process_handle, u64 addr) { - - auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + SharedPtr<Process> process = handle_table.Get<Process>(process_handle); if (!process) { return ERR_INVALID_HANDLE; } auto vma = process->VMManager().FindVMA(addr); memory_info->attributes = 0; - if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) { + if (vma == process->VMManager().vma_map.end()) { memory_info->base_address = 0; memory_info->permission = static_cast<u32>(VMAPermission::None); memory_info->size = 0; @@ -695,20 +692,19 @@ static void ExitProcess() { /// Creates a new thread static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, u32 priority, s32 processor_id) { - std::string name = fmt::format("thread-{:X}", entry_point); - if (priority > THREADPRIO_LOWEST) { return ERR_INVALID_THREAD_PRIORITY; } - const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); + auto* const current_process = Core::CurrentProcess(); + const ResourceLimit& resource_limit = current_process->GetResourceLimit(); if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } if (processor_id == THREADPROCESSORID_DEFAULT) { // Set the target CPU to the one specified in the process' exheader. - processor_id = Core::CurrentProcess()->GetDefaultProcessorID(); + processor_id = current_process->GetDefaultProcessorID(); ASSERT(processor_id != THREADPROCESSORID_DEFAULT); } @@ -723,11 +719,13 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V return ERR_INVALID_PROCESSOR_ID; } + const std::string name = fmt::format("thread-{:X}", entry_point); auto& kernel = Core::System::GetInstance().Kernel(); CASCADE_RESULT(SharedPtr<Thread> thread, Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, - *Core::CurrentProcess())); - const auto new_guest_handle = kernel.HandleTable().Create(thread); + *current_process)); + + const auto new_guest_handle = current_process->GetHandleTable().Create(thread); if (new_guest_handle.Failed()) { return new_guest_handle.Code(); } @@ -748,8 +746,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V static ResultCode StartThread(Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { return ERR_INVALID_HANDLE; } @@ -796,8 +794,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", mutex_addr, condition_variable_addr, thread_handle, nano_seconds); - auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); ASSERT(thread); CASCADE_CODE(Mutex::Release(mutex_addr)); @@ -908,9 +906,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target mutex_val | Mutex::MutexHasWaitersFlag)); // The mutex is already owned by some other thread, make this thread wait on it. - auto& kernel = Core::System::GetInstance().Kernel(); - Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); - auto owner = kernel.HandleTable().Get<Thread>(owner_handle); + const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + auto owner = handle_table.Get<Thread>(owner_handle); ASSERT(owner); ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); thread->InvalidateWakeupCallback(); @@ -989,16 +987,16 @@ static u64 GetSystemTick() { static ResultCode CloseHandle(Handle handle) { LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); - auto& kernel = Core::System::GetInstance().Kernel(); - return kernel.HandleTable().Close(handle); + auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + return handle_table.Close(handle); } /// Reset an event static ResultCode ResetSignal(Handle handle) { LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); - auto& kernel = Core::System::GetInstance().Kernel(); - auto event = kernel.HandleTable().Get<Event>(handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + auto event = handle_table.Get<Event>(handle); ASSERT(event != nullptr); @@ -1017,8 +1015,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { return ERR_INVALID_HANDLE; } @@ -1033,8 +1031,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, mask, core); - auto& kernel = Core::System::GetInstance().Kernel(); - const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { return ERR_INVALID_HANDLE; } @@ -1095,7 +1093,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss } auto& kernel = Core::System::GetInstance().Kernel(); - auto& handle_table = kernel.HandleTable(); + auto& handle_table = Core::CurrentProcess()->GetHandleTable(); auto shared_mem_handle = SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, local_perms, remote_perms); @@ -1107,10 +1105,12 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss static ResultCode ClearEvent(Handle handle) { LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); - auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle); - if (evt == nullptr) + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + SharedPtr<Event> evt = handle_table.Get<Event>(handle); + if (evt == nullptr) { return ERR_INVALID_HANDLE; + } + evt->Clear(); return RESULT_SUCCESS; } @@ -1123,8 +1123,8 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { Status, }; - const auto& kernel = Core::System::GetInstance().Kernel(); - const auto process = kernel.HandleTable().Get<Process>(process_handle); + const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto process = handle_table.Get<Process>(process_handle); if (!process) { return ERR_INVALID_HANDLE; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 35ec98c1a..59bc9e0af 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -266,7 +266,7 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); // Register 1 must be a handle to the main thread - const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap(); + const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap(); thread->SetGuestHandle(guest_handle); thread->GetContext().cpu_registers[1] = guest_handle; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4d1f83170..ecf72ae24 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -15,6 +15,7 @@ #include "core/hle/service/am/idle.h" #include "core/hle/service/am/omm.h" #include "core/hle/service/am/spsm.h" +#include "core/hle/service/am/tcap.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -26,13 +27,18 @@ namespace Service::AM { IWindowController::IWindowController() : ServiceFramework("IWindowController") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CreateWindow"}, {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, {11, nullptr, "ReleaseForegroundRights"}, {12, nullptr, "RejectToChangeIntoBackground"}, + {20, nullptr, "SetAppletWindowVisibility"}, + {21, nullptr, "SetAppletGpuTimeSlice"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -87,6 +93,7 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo } IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetLastForegroundCaptureImage"}, {1, nullptr, "UpdateLastForegroundCaptureImage"}, @@ -117,7 +124,11 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController" {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, + // 6.0.0+ + {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -128,6 +139,7 @@ IDebugFunctions::~IDebugFunctions() = default; ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "Exit"}, {1, &ISelfController::LockExit, "LockExit"}, @@ -136,10 +148,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger {4, nullptr, "LeaveFatalSection"}, {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, - {11, &ISelfController::SetOperationModeChangedNotification, - "SetOperationModeChangedNotification"}, - {12, &ISelfController::SetPerformanceModeChangedNotification, - "SetPerformanceModeChangedNotification"}, + {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"}, + {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"}, {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, {15, nullptr, "SetScreenShotAppletIdentityInfo"}, @@ -165,7 +175,12 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger {69, nullptr, "IsAutoSleepDisabled"}, {70, nullptr, "ReportMultimediaError"}, {80, nullptr, "SetWirelessPriorityMode"}, + {90, nullptr, "GetAccumulatedSuspendedTickValue"}, + {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"}, + {1000, nullptr, "GetDebugStorageChannel"}, }; + // clang-format on + RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); @@ -312,6 +327,7 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c } ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { + // clang-format off static const FunctionInfo functions[] = { {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, @@ -336,11 +352,12 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter" {52, nullptr, "SwitchLcdBacklight"}, {55, nullptr, "IsInControllerFirmwareUpdateSection"}, {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, - {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, - "GetDefaultDisplayResolutionChangeEvent"}, + {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, {62, nullptr, "GetHdcpAuthenticationState"}, {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, }; + // clang-format on + RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); @@ -432,11 +449,14 @@ class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { public: explicit IStorageAccessor(std::vector<u8> buffer) : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { + // clang-format off static const FunctionInfo functions[] = { {0, &IStorageAccessor::GetSize, "GetSize"}, {10, &IStorageAccessor::Write, "Write"}, {11, &IStorageAccessor::Read, "Read"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -489,10 +509,13 @@ class IStorage final : public ServiceFramework<IStorage> { public: explicit IStorage(std::vector<u8> buffer) : ServiceFramework("IStorage"), buffer(std::move(buffer)) { + // clang-format off static const FunctionInfo functions[] = { {0, &IStorage::Open, "Open"}, {1, nullptr, "OpenTransferStorage"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -512,6 +535,7 @@ private: class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { public: explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { + // clang-format off static const FunctionInfo functions[] = { {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, {1, nullptr, "IsCompleted"}, @@ -532,6 +556,8 @@ public: {150, nullptr, "RequestForAppletToGetForeground"}, {160, nullptr, "GetIndirectLayerConsumerHandle"}, }; + // clang-format on + RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); @@ -624,13 +650,13 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { } IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { + // clang-format off static const FunctionInfo functions[] = { {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, {10, nullptr, "CreateApplicationAndPushAndRequestToStart"}, {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, {12, nullptr, "CreateApplicationAndRequestToStart"}, - {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, - "CreateApplicationAndRequestToStartForQuest"}, + {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"}, {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, @@ -638,10 +664,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {24, nullptr, "GetLaunchStorageInfoForDebug"}, {25, nullptr, "ExtendSaveData"}, {26, nullptr, "GetSaveDataSize"}, - {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, - "BeginBlockingHomeButtonShortAndLongPressed"}, - {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, - "EndBlockingHomeButtonShortAndLongPressed"}, + {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, + {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, @@ -666,6 +690,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {1000, nullptr, "CreateMovieMaker"}, {1001, nullptr, "PrepareForJit"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -804,9 +830,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager, std::make_shared<IdleSys>()->InstallAsService(service_manager); std::make_shared<OMM>()->InstallAsService(service_manager); std::make_shared<SPSM>()->InstallAsService(service_manager); + std::make_shared<TCAP>()->InstallAsService(service_manager); } IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") { + // clang-format off static const FunctionInfo functions[] = { {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"}, {11, nullptr, "LockForeground"}, @@ -815,7 +843,10 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions" {21, nullptr, "GetPopFromGeneralChannelEvent"}, {30, nullptr, "GetHomeButtonWriterLockAccessor"}, {31, nullptr, "GetWriterLockAccessorEx"}, + {100, nullptr, "PopRequestLaunchApplicationForDebug"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -828,6 +859,7 @@ void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) } IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "RequestToEnterSleep"}, {1, nullptr, "EnterSleep"}, @@ -841,18 +873,23 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat {14, nullptr, "ShouldSleepOnBoot"}, {15, nullptr, "GetHdcpAuthenticationFailedEvent"}, }; + // clang-format on + RegisterHandlers(functions); } IGlobalStateController::~IGlobalStateController() = default; IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CreateApplication"}, {1, nullptr, "PopLaunchRequestedApplication"}, {10, nullptr, "CreateSystemApplication"}, {100, nullptr, "PopFloatingApplicationForDevelopment"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -860,6 +897,7 @@ IApplicationCreator::~IApplicationCreator() = default; IProcessWindingController::IProcessWindingController() : ServiceFramework("IProcessWindingController") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetLaunchReason"}, {11, nullptr, "OpenCallingLibraryApplet"}, @@ -870,6 +908,8 @@ IProcessWindingController::IProcessWindingController() {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, {41, nullptr, "ReserveToStartAndWait"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 4296c255e..68ea778e8 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -211,6 +211,7 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { + // clang-format off static const FunctionInfo functions[] = { {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"}, @@ -218,7 +219,10 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {300, nullptr, "OpenOverlayAppletProxy"}, {350, nullptr, "OpenSystemApplicationProxy"}, {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"}, + {401, nullptr, "GetSystemAppletControllerForDebug"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index e45cf6e20..60717afd9 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -14,6 +14,7 @@ class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { public: explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { + // clang-format off static const FunctionInfo functions[] = { {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, {1, &IApplicationProxy::GetSelfController, "GetSelfController"}, @@ -25,6 +26,8 @@ public: {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"}, {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp index 0e3088bc8..f814fe2c0 100644 --- a/src/core/hle/service/am/idle.cpp +++ b/src/core/hle/service/am/idle.cpp @@ -12,9 +12,9 @@ IdleSys::IdleSys() : ServiceFramework{"idle:sys"} { {0, nullptr, "GetAutoPowerDownEvent"}, {1, nullptr, "Unknown1"}, {2, nullptr, "Unknown2"}, - {3, nullptr, "Unknown3"}, - {4, nullptr, "Unknown4"}, - {5, nullptr, "Unknown5"}, + {3, nullptr, "SetHandlingContext"}, + {4, nullptr, "LoadAndApplySettings"}, + {5, nullptr, "ReportUserIsActive"}, }; // clang-format on diff --git a/src/core/hle/service/am/tcap.cpp b/src/core/hle/service/am/tcap.cpp new file mode 100644 index 000000000..a75cbdda8 --- /dev/null +++ b/src/core/hle/service/am/tcap.cpp @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/am/tcap.h" + +namespace Service::AM { + +TCAP::TCAP() : ServiceFramework{"tcap"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetContinuousHighSkinTemperatureEvent"}, + {1, nullptr, "SetOperationMode"}, + {2, nullptr, "LoadAndApplySettings"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +TCAP::~TCAP() = default; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/tcap.h b/src/core/hle/service/am/tcap.h new file mode 100644 index 000000000..2021b55d1 --- /dev/null +++ b/src/core/hle/service/am/tcap.h @@ -0,0 +1,17 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class TCAP final : public ServiceFramework<TCAP> { +public: + explicit TCAP(); + ~TCAP() override; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index b26593b4f..b06e65a77 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -26,7 +26,11 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff; constexpr s32 HID_JOYSTICK_MIN = -0x7fff; constexpr std::size_t NPAD_OFFSET = 0x9A00; constexpr u32 BATTERY_FULL = 2; - +constexpr u32 NPAD_HANDHELD = 32; +constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this? +constexpr u32 MAX_NPAD_ID = 7; +constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER = + Controller_NPad::NPadControllerType::JoyDual; constexpr std::array<u32, 10> npad_id_list{ 0, 1, 2, 3, 4, 5, 6, 7, 32, 16, }; @@ -121,7 +125,7 @@ void Controller_NPad::OnInit() { supported_npad_id_types.resize(npad_id_list.size()); std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), npad_id_list.size() * sizeof(u32)); - AddNewController(NPadControllerType::JoyDual); + AddNewController(PREFERRED_CONTROLLER); } } @@ -218,6 +222,51 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); + if (controller_type == NPadControllerType::JoyLeft || + controller_type == NPadControllerType::JoyRight) { + if (npad.properties.is_horizontal) { + ControllerPadState state{}; + AnalogPosition temp_lstick_entry{}; + AnalogPosition temp_rstick_entry{}; + if (controller_type == NPadControllerType::JoyLeft) { + state.d_down.Assign(pad_state.d_left.Value()); + state.d_left.Assign(pad_state.d_up.Value()); + state.d_right.Assign(pad_state.d_down.Value()); + state.d_up.Assign(pad_state.d_right.Value()); + state.l.Assign(pad_state.l.Value() | pad_state.sl.Value()); + state.r.Assign(pad_state.r.Value() | pad_state.sr.Value()); + + state.zl.Assign(pad_state.zl.Value()); + state.plus.Assign(pad_state.minus.Value()); + + temp_lstick_entry = lstick_entry; + temp_rstick_entry = rstick_entry; + std::swap(temp_lstick_entry.x, temp_lstick_entry.y); + std::swap(temp_rstick_entry.x, temp_rstick_entry.y); + temp_lstick_entry.y *= -1; + } else if (controller_type == NPadControllerType::JoyRight) { + state.x.Assign(pad_state.a.Value()); + state.a.Assign(pad_state.b.Value()); + state.b.Assign(pad_state.y.Value()); + state.y.Assign(pad_state.b.Value()); + + state.l.Assign(pad_state.l.Value() | pad_state.sl.Value()); + state.r.Assign(pad_state.r.Value() | pad_state.sr.Value()); + state.zr.Assign(pad_state.zr.Value()); + state.plus.Assign(pad_state.plus.Value()); + + temp_lstick_entry = lstick_entry; + temp_rstick_entry = rstick_entry; + std::swap(temp_lstick_entry.x, temp_lstick_entry.y); + std::swap(temp_rstick_entry.x, temp_rstick_entry.y); + temp_rstick_entry.x *= -1; + } + pad_state.raw = state.raw; + lstick_entry = temp_lstick_entry; + rstick_entry = temp_rstick_entry; + } + } + auto& main_controller = npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; auto& handheld_entry = @@ -320,6 +369,16 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { supported_npad_id_types.clear(); supported_npad_id_types.resize(length / sizeof(u32)); std::memcpy(supported_npad_id_types.data(), data, length); + for (std::size_t i = 0; i < connected_controllers.size(); i++) { + auto& controller = connected_controllers[i]; + if (!controller.is_connected) { + continue; + } + if (!IsControllerSupported(PREFERRED_CONTROLLER)) { + controller.type = DecideBestController(PREFERRED_CONTROLLER); + InitNewlyAddedControler(i); + } + } } void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { @@ -351,11 +410,11 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, for (std::size_t i = 0; i < controller_ids.size(); i++) { std::size_t controller_pos = i; // Handheld controller conversion - if (controller_pos == 32) { + if (controller_pos == NPAD_HANDHELD) { controller_pos = 8; } // Unknown controller conversion - if (controller_pos == 16) { + if (controller_pos == NPAD_UNKNOWN) { controller_pos = 9; } if (connected_controllers[controller_pos].is_connected) { @@ -433,4 +492,128 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { can_controllers_vibrate = can_vibrate; } + +bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { + const bool support_handheld = + std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) != + supported_npad_id_types.end(); + if (controller == NPadControllerType::Handheld) { + // Handheld is not even a supported type, lets stop here + if (!support_handheld) { + return false; + } + // Handheld should not be supported in docked mode + if (Settings::values.use_docked_mode) { + return false; + } + + return true; + } + if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(), + [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) { + switch (controller) { + case NPadControllerType::ProController: + return style.pro_controller; + case NPadControllerType::JoyDual: + return style.joycon_dual; + case NPadControllerType::JoyLeft: + return style.joycon_left; + case NPadControllerType::JoyRight: + return style.joycon_right; + case NPadControllerType::Pokeball: + return style.pokeball; + default: + return false; + } + } + return false; +} + +Controller_NPad::NPadControllerType Controller_NPad::DecideBestController( + NPadControllerType priority) const { + if (IsControllerSupported(priority)) { + return priority; + } + const auto is_docked = Settings::values.use_docked_mode; + if (is_docked && priority == NPadControllerType::Handheld) { + priority = NPadControllerType::JoyDual; + if (IsControllerSupported(priority)) { + return priority; + } + } + std::vector<NPadControllerType> priority_list; + switch (priority) { + case NPadControllerType::ProController: + priority_list.push_back(NPadControllerType::JoyDual); + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::Pokeball); + break; + case NPadControllerType::Handheld: + priority_list.push_back(NPadControllerType::JoyDual); + priority_list.push_back(NPadControllerType::ProController); + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::Pokeball); + break; + case NPadControllerType::JoyDual: + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::ProController); + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::Pokeball); + break; + case NPadControllerType::JoyLeft: + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::JoyDual); + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::ProController); + priority_list.push_back(NPadControllerType::Pokeball); + break; + case NPadControllerType::JoyRight: + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyDual); + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::ProController); + priority_list.push_back(NPadControllerType::Pokeball); + break; + case NPadControllerType::Pokeball: + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::JoyDual); + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::ProController); + break; + default: + priority_list.push_back(NPadControllerType::JoyDual); + if (!is_docked) { + priority_list.push_back(NPadControllerType::Handheld); + } + priority_list.push_back(NPadControllerType::ProController); + priority_list.push_back(NPadControllerType::JoyLeft); + priority_list.push_back(NPadControllerType::JoyRight); + priority_list.push_back(NPadControllerType::JoyDual); + } + + const auto iter = std::find_if(priority_list.begin(), priority_list.end(), + [this](auto type) { return IsControllerSupported(type); }); + if (iter == priority_list.end()) { + UNIMPLEMENTED_MSG("Could not find supported controller!"); + return priority; + } + + return *iter; +} + } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 7c0f93acf..ac86985ff 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -283,5 +283,7 @@ private: bool can_controllers_vibrate{true}; void InitNewlyAddedControler(std::size_t controller_idx); + bool IsControllerSupported(NPadControllerType controller) const; + NPadControllerType DecideBestController(NPadControllerType priority) const; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index beb89218a..a9aa9ec78 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -827,6 +827,7 @@ public: {11, nullptr, "EnableJoyPollingReceiveMode"}, {12, nullptr, "DisableJoyPollingReceiveMode"}, {13, nullptr, "GetPollingData"}, + {14, nullptr, "SetStatusManagerType"}, }; // clang-format on diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index 7321584e1..164c57e18 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp @@ -18,35 +18,35 @@ public: explicit LBL() : ServiceFramework{"lbl"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Unknown1"}, - {1, nullptr, "Unknown2"}, - {2, nullptr, "Unknown3"}, - {3, nullptr, "GetCurrentBacklightLevel"}, - {4, nullptr, "Unknown4"}, - {5, nullptr, "GetAlsComputedBacklightLevel"}, - {6, nullptr, "TurnOffBacklight"}, - {7, nullptr, "TurnOnBacklight"}, - {8, nullptr, "GetBacklightStatus"}, - {9, nullptr, "Unknown5"}, - {10, nullptr, "Unknown6"}, - {11, nullptr, "Unknown7"}, - {12, nullptr, "Unknown8"}, - {13, nullptr, "Unknown9"}, - {14, nullptr, "Unknown10"}, - {15, nullptr, "GetAutoBrightnessSetting"}, - {16, nullptr, "ReadRawLightSensor"}, - {17, nullptr, "Unknown11"}, - {18, nullptr, "Unknown12"}, - {19, nullptr, "Unknown13"}, - {20, nullptr, "Unknown14"}, - {21, nullptr, "Unknown15"}, - {22, nullptr, "Unknown16"}, - {23, nullptr, "Unknown17"}, - {24, nullptr, "Unknown18"}, - {25, nullptr, "Unknown19"}, + {0, nullptr, "SaveCurrentSetting"}, + {1, nullptr, "LoadCurrentSetting"}, + {2, nullptr, "SetCurrentBrightnessSetting"}, + {3, nullptr, "GetCurrentBrightnessSetting"}, + {4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"}, + {5, nullptr, "GetBrightnessSettingAppliedToBacklight"}, + {6, nullptr, "SwitchBacklightOn"}, + {7, nullptr, "SwitchBacklightOff"}, + {8, nullptr, "GetBacklightSwitchStatus"}, + {9, nullptr, "EnableDimming"}, + {10, nullptr, "DisableDimming"}, + {11, nullptr, "IsDimmingEnabled"}, + {12, nullptr, "EnableAutoBrightnessControl"}, + {13, nullptr, "DisableAutoBrightnessControl"}, + {14, nullptr, "IsAutoBrightnessControlEnabled"}, + {15, nullptr, "SetAmbientLightSensorValue"}, + {16, nullptr, "GetAmbientLightSensorValue"}, + {17, nullptr, "SetBrightnessReflectionDelayLevel"}, + {18, nullptr, "GetBrightnessReflectionDelayLevel"}, + {19, nullptr, "SetCurrentBrightnessMapping"}, + {20, nullptr, "GetCurrentBrightnessMapping"}, + {21, nullptr, "SetCurrentAmbientLightSensorMapping"}, + {22, nullptr, "GetCurrentAmbientLightSensorMapping"}, + {23, nullptr, "IsAmbientLightSensorAvailable"}, + {24, nullptr, "SetCurrentBrightnessSettingForVrMode"}, + {25, nullptr, "GetCurrentBrightnessSettingForVrMode"}, {26, &LBL::EnableVrMode, "EnableVrMode"}, {27, &LBL::DisableVrMode, "DisableVrMode"}, - {28, &LBL::GetVrMode, "GetVrMode"}, + {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"}, }; // clang-format on @@ -72,7 +72,7 @@ private: LOG_DEBUG(Service_LBL, "called"); } - void GetVrMode(Kernel::HLERequestContext& ctx) { + void IsVrModeEnabled(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(vr_mode_enabled); diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp new file mode 100644 index 000000000..ccb6f9da9 --- /dev/null +++ b/src/core/hle/service/npns/npns.cpp @@ -0,0 +1,88 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/npns/npns.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::NPNS { + +class NPNS_S final : public ServiceFramework<NPNS_S> { +public: + explicit NPNS_S() : ServiceFramework{"npns:s"} { + // clang-format off + static const FunctionInfo functions[] = { + {1, nullptr, "ListenAll"}, + {2, nullptr, "ListenTo"}, + {3, nullptr, "Receive"}, + {4, nullptr, "ReceiveRaw"}, + {5, nullptr, "GetReceiveEvent"}, + {6, nullptr, "ListenUndelivered"}, + {7, nullptr, "GetStateChangeEVent"}, + {11, nullptr, "SubscribeTopic"}, + {12, nullptr, "UnsubscribeTopic"}, + {13, nullptr, "QueryIsTopicExist"}, + {21, nullptr, "CreateToken"}, + {22, nullptr, "CreateTokenWithApplicationId"}, + {23, nullptr, "DestroyToken"}, + {24, nullptr, "DestroyTokenWithApplicationId"}, + {25, nullptr, "QueryIsTokenValid"}, + {31, nullptr, "UploadTokenToBaaS"}, + {32, nullptr, "DestroyTokenForBaaS"}, + {33, nullptr, "CreateTokenForBaaS"}, + {34, nullptr, "SetBaaSDeviceAccountIdList"}, + {101, nullptr, "Suspend"}, + {102, nullptr, "Resume"}, + {103, nullptr, "GetState"}, + {104, nullptr, "GetStatistics"}, + {105, nullptr, "GetPlayReportRequestEvent"}, + {111, nullptr, "GetJid"}, + {112, nullptr, "CreateJid"}, + {113, nullptr, "DestroyJid"}, + {114, nullptr, "AttachJid"}, + {115, nullptr, "DetachJid"}, + {201, nullptr, "RequestChangeStateForceTimed"}, + {102, nullptr, "RequestChangeStateForceAsync"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NPNS_U final : public ServiceFramework<NPNS_U> { +public: + explicit NPNS_U() : ServiceFramework{"npns:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {1, nullptr, "ListenAll"}, + {2, nullptr, "ListenTo"}, + {3, nullptr, "Receive"}, + {4, nullptr, "ReceiveRaw"}, + {5, nullptr, "GetReceiveEvent"}, + {7, nullptr, "GetStateChangeEVent"}, + {21, nullptr, "CreateToken"}, + {23, nullptr, "DestroyToken"}, + {25, nullptr, "QueryIsTokenValid"}, + {26, nullptr, "ListenToMyApplicationId"}, + {101, nullptr, "Suspend"}, + {102, nullptr, "Resume"}, + {103, nullptr, "GetState"}, + {104, nullptr, "GetStatistics"}, + {111, nullptr, "GetJid"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<NPNS_S>()->InstallAsService(sm); + std::make_shared<NPNS_U>()->InstallAsService(sm); +} + +} // namespace Service::NPNS diff --git a/src/core/hle/service/npns/npns.h b/src/core/hle/service/npns/npns.h new file mode 100644 index 000000000..861cd3e48 --- /dev/null +++ b/src/core/hle/service/npns/npns.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::NPNS { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::NPNS diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 6a9eccfb5..e4fcee9f8 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -14,20 +14,24 @@ public: explicit PlayReport(const char* name) : ServiceFramework{name} { // clang-format off static const FunctionInfo functions[] = { - {10100, nullptr, "SaveReport"}, - {10101, &PlayReport::SaveReportWithUser, "SaveReportWithUser"}, + {10100, nullptr, "SaveReportOld"}, + {10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"}, + {10102, nullptr, "SaveReport"}, + {10103, nullptr, "SaveReportWithUser"}, {10200, nullptr, "RequestImmediateTransmission"}, {10300, nullptr, "GetTransmissionStatus"}, {20100, nullptr, "SaveSystemReport"}, - {20200, nullptr, "SetOperationMode"}, {20101, nullptr, "SaveSystemReportWithUser"}, + {20200, nullptr, "SetOperationMode"}, {30100, nullptr, "ClearStorage"}, + {30200, nullptr, "ClearStatistics"}, + {30300, nullptr, "GetStorageUsage"}, + {30400, nullptr, "GetStatistics"}, + {30401, nullptr, "GetThroughputHistory"}, + {30500, nullptr, "GetLastUploadError"}, {40100, nullptr, "IsUserAgreementCheckEnabled"}, {40101, nullptr, "SetUserAgreementCheckEnabled"}, - {90100, nullptr, "GetStorageUsage"}, - {90200, nullptr, "GetStatistics"}, - {90201, nullptr, "GetThroughputHistory"}, - {90300, nullptr, "GetLastUploadError"}, + {90100, nullptr, "ReadAllReportFiles"}, }; // clang-format on @@ -35,7 +39,7 @@ public: } private: - void SaveReportWithUser(Kernel::HLERequestContext& ctx) { + void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) { // TODO(ogniK): Do we want to add play report? LOG_WARNING(Service_PREPO, "(STUBBED) called"); @@ -46,6 +50,7 @@ private: void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager); + std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager); std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager); std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager); std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager); diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp new file mode 100644 index 000000000..c2d5fda94 --- /dev/null +++ b/src/core/hle/service/ptm/psm.cpp @@ -0,0 +1,71 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_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 { + +constexpr u32 BATTERY_FULLY_CHARGED = 100; // 100% Full +constexpr u32 BATTERY_CURRENTLY_CHARGING = 1; // Plugged into an official dock + +class PSM final : public ServiceFramework<PSM> { +public: + explicit PSM() : ServiceFramework{"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, nullptr, "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"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + + ~PSM() override = default; + +private: + void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_PSM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(BATTERY_FULLY_CHARGED); + } + + void GetChargerType(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_PSM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(BATTERY_CURRENTLY_CHARGING); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<PSM>()->InstallAsService(sm); +} + +} // namespace Service::PSM diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h new file mode 100644 index 000000000..a286793ae --- /dev/null +++ b/src/core/hle/service/ptm/psm.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::PSM { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::PSM diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index a225cb4cb..a4cf45267 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -22,7 +22,7 @@ #include "core/hle/service/apm/apm.h" #include "core/hle/service/arp/arp.h" #include "core/hle/service/audio/audio.h" -#include "core/hle/service/bcat/bcat.h" +#include "core/hle/service/bcat/module.h" #include "core/hle/service/bpc/bpc.h" #include "core/hle/service/btdrv/btdrv.h" #include "core/hle/service/btm/btm.h" @@ -48,15 +48,17 @@ #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/nim/nim.h" +#include "core/hle/service/npns/npns.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/pcie/pcie.h" -#include "core/hle/service/pctl/pctl.h" +#include "core/hle/service/pctl/module.h" #include "core/hle/service/pcv/pcv.h" #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/service.h" #include "core/hle/service/set/settings.h" #include "core/hle/service/sm/sm.h" @@ -236,6 +238,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) NFP::InstallInterfaces(*sm); NIFM::InstallInterfaces(*sm); NIM::InstallInterfaces(*sm); + NPNS::InstallInterfaces(*sm); NS::InstallInterfaces(*sm); Nvidia::InstallInterfaces(*sm, *nv_flinger); PCIe::InstallInterfaces(*sm); @@ -244,6 +247,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) PlayReport::InstallInterfaces(*sm); PM::InstallInterfaces(*sm); PSC::InstallInterfaces(*sm); + PSM::InstallInterfaces(*sm); Set::InstallInterfaces(*sm); Sockets::InstallInterfaces(*sm); SPL::InstallInterfaces(*sm); |