summaryrefslogtreecommitdiffstats
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp173
-rw-r--r--src/core/hle/kernel/address_arbiter.h32
-rw-r--r--src/core/hle/kernel/errors.h12
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp5
-rw-r--r--src/core/hle/kernel/mutex.cpp4
-rw-r--r--src/core/hle/kernel/svc.cpp68
-rw-r--r--src/core/hle/kernel/svc_wrap.h14
-rw-r--r--src/core/hle/kernel/thread.cpp6
-rw-r--r--src/core/hle/kernel/thread.h4
-rw-r--r--src/core/hle/service/am/am.cpp13
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/audio/audren_u.cpp105
-rw-r--r--src/core/hle/service/audio/audren_u.h6
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp31
-rw-r--r--src/core/hle/service/hid/hid.cpp7
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/mm/mm_u.cpp50
-rw-r--r--src/core/hle/service/mm/mm_u.h29
-rw-r--r--src/core/hle/service/nfp/nfp.cpp108
-rw-r--r--src/core/hle/service/nifm/nifm.cpp11
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp22
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h31
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp3
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/set/set.cpp5
25 files changed, 645 insertions, 99 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
new file mode 100644
index 000000000..e9c8369d7
--- /dev/null
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -0,0 +1,173 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/core.h"
+#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/lock.h"
+#include "core/memory.h"
+
+namespace Kernel {
+namespace AddressArbiter {
+
+// Performs actual address waiting logic.
+static ResultCode WaitForAddress(VAddr address, s64 timeout) {
+ SharedPtr<Thread> current_thread = GetCurrentThread();
+ current_thread->arb_wait_address = address;
+ current_thread->status = THREADSTATUS_WAIT_ARB;
+ current_thread->wakeup_callback = nullptr;
+
+ current_thread->WakeAfterDelay(timeout);
+
+ Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
+ return RESULT_TIMEOUT;
+}
+
+// Gets the threads waiting on an address.
+static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads,
+ VAddr address) {
+ auto RetrieveWaitingThreads =
+ [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
+ const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
+ auto& thread_list = scheduler->GetThreadList();
+
+ for (auto& thread : thread_list) {
+ if (thread->arb_wait_address == arb_addr)
+ waiting_threads.push_back(thread);
+ }
+ };
+
+ // Retrieve a list of all threads that are waiting for this address.
+ RetrieveWaitingThreads(0, waiting_threads, address);
+ RetrieveWaitingThreads(1, waiting_threads, address);
+ RetrieveWaitingThreads(2, waiting_threads, address);
+ RetrieveWaitingThreads(3, waiting_threads, address);
+ // Sort them by priority, such that the highest priority ones come first.
+ std::sort(waiting_threads.begin(), waiting_threads.end(),
+ [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
+ return lhs->current_priority < rhs->current_priority;
+ });
+}
+
+// Wake up num_to_wake (or all) threads in a vector.
+static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
+ // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
+ // them all.
+ size_t last = waiting_threads.size();
+ if (num_to_wake > 0)
+ last = num_to_wake;
+
+ // Signal the waiting threads.
+ for (size_t i = 0; i < last; i++) {
+ ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB);
+ waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
+ waiting_threads[i]->arb_wait_address = 0;
+ waiting_threads[i]->ResumeFromWait();
+ }
+}
+
+// Signals an address being waited on.
+ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
+ // Get threads waiting on the address.
+ std::vector<SharedPtr<Thread>> waiting_threads;
+ GetThreadsWaitingOnAddress(waiting_threads, address);
+
+ WakeThreads(waiting_threads, num_to_wake);
+ return RESULT_SUCCESS;
+}
+
+// Signals an address being waited on and increments its value if equal to the value argument.
+ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) {
+ // Ensure that we can write to the address.
+ if (!Memory::IsValidVirtualAddress(address)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (static_cast<s32>(Memory::Read32(address)) == value) {
+ Memory::Write32(address, static_cast<u32>(value + 1));
+ } else {
+ return ERR_INVALID_STATE;
+ }
+
+ return SignalToAddress(address, num_to_wake);
+}
+
+// Signals an address being waited on and modifies its value based on waiting thread count if equal
+// to the value argument.
+ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
+ s32 num_to_wake) {
+ // Ensure that we can write to the address.
+ if (!Memory::IsValidVirtualAddress(address)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ // Get threads waiting on the address.
+ std::vector<SharedPtr<Thread>> waiting_threads;
+ GetThreadsWaitingOnAddress(waiting_threads, address);
+
+ // Determine the modified value depending on the waiting count.
+ s32 updated_value;
+ if (waiting_threads.size() == 0) {
+ updated_value = value - 1;
+ } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) {
+ updated_value = value + 1;
+ } else {
+ updated_value = value;
+ }
+
+ if (static_cast<s32>(Memory::Read32(address)) == value) {
+ Memory::Write32(address, static_cast<u32>(updated_value));
+ } else {
+ return ERR_INVALID_STATE;
+ }
+
+ WakeThreads(waiting_threads, num_to_wake);
+ return RESULT_SUCCESS;
+}
+
+// Waits on an address if the value passed is less than the argument value, optionally decrementing.
+ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) {
+ // Ensure that we can read the address.
+ if (!Memory::IsValidVirtualAddress(address)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ s32 cur_value = static_cast<s32>(Memory::Read32(address));
+ if (cur_value < value) {
+ Memory::Write32(address, static_cast<u32>(cur_value - 1));
+ } else {
+ return ERR_INVALID_STATE;
+ }
+ // Short-circuit without rescheduling, if timeout is zero.
+ if (timeout == 0) {
+ return RESULT_TIMEOUT;
+ }
+
+ return WaitForAddress(address, timeout);
+}
+
+// Waits on an address if the value passed is equal to the argument value.
+ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
+ // Ensure that we can read the address.
+ if (!Memory::IsValidVirtualAddress(address)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+ // Only wait for the address if equal.
+ if (static_cast<s32>(Memory::Read32(address)) != value) {
+ return ERR_INVALID_STATE;
+ }
+ // Short-circuit without rescheduling, if timeout is zero.
+ if (timeout == 0) {
+ return RESULT_TIMEOUT;
+ }
+
+ return WaitForAddress(address, timeout);
+}
+} // namespace AddressArbiter
+} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
new file mode 100644
index 000000000..f20f3dbc0
--- /dev/null
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -0,0 +1,32 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+namespace AddressArbiter {
+enum class ArbitrationType {
+ WaitIfLessThan = 0,
+ DecrementAndWaitIfLessThan = 1,
+ WaitIfEqual = 2,
+};
+
+enum class SignalType {
+ Signal = 0,
+ IncrementAndSignalIfEqual = 1,
+ ModifyByWaitingCountAndSignalIfEqual = 2,
+};
+
+ResultCode SignalToAddress(VAddr address, s32 num_to_wake);
+ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
+ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
+
+ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement);
+ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
+} // namespace AddressArbiter
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index e1b5430bf..221cb1bb5 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -20,13 +20,16 @@ enum {
MaxConnectionsReached = 52,
// Confirmed Switch OS error codes
- MisalignedAddress = 102,
+ InvalidAddress = 102,
+ InvalidMemoryState = 106,
InvalidProcessorId = 113,
InvalidHandle = 114,
InvalidCombination = 116,
Timeout = 117,
SynchronizationCanceled = 118,
TooLarge = 119,
+ InvalidEnumValue = 120,
+ InvalidState = 125,
};
}
@@ -39,14 +42,15 @@ constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(-1);
constexpr ResultCode ERR_WRONG_PERMISSION(-1);
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1);
-constexpr ResultCode ERR_INVALID_ENUM_VALUE(-1);
+constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
constexpr ResultCode ERR_INVALID_COMBINATION(-1);
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(-1);
constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
-constexpr ResultCode ERR_INVALID_ADDRESS(-1);
-constexpr ResultCode ERR_INVALID_ADDRESS_STATE(-1);
+constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
+constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
+constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
constexpr ResultCode ERR_INVALID_POINTER(-1);
constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
constexpr ResultCode ERR_NOT_AUTHORIZED(-1);
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 01904467e..b0d83f401 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -271,6 +271,11 @@ std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
}
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffer_index) const {
+ if (size == 0) {
+ NGLOG_WARNING(Core, "skip empty buffer write");
+ return 0;
+ }
+
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()};
const size_t buffer_size{GetWriteBufferSize(buffer_index)};
if (size > buffer_size) {
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index bc144f3de..65560226d 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -59,7 +59,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
- return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
+ return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress);
}
SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle);
@@ -97,7 +97,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
ResultCode Mutex::Release(VAddr address) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
- return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
+ return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress);
}
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index ec3601e8b..1a36e0d02 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -11,6 +11,7 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/event.h"
@@ -316,6 +317,11 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
"(STUBBED) Attempted to query privileged process id bounds, returned 0");
*result = 0;
break;
+ case GetInfoType::UserExceptionContextAddr:
+ NGLOG_WARNING(Kernel_SVC,
+ "(STUBBED) Attempted to query user exception context address, returned 0");
+ *result = 0;
+ break;
default:
UNIMPLEMENTED();
}
@@ -575,7 +581,7 @@ static void SleepThread(s64 nanoseconds) {
Core::System::GetInstance().PrepareReschedule();
}
-/// Signal process wide key atomic
+/// Wait process wide key atomic
static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr,
Handle thread_handle, s64 nano_seconds) {
NGLOG_TRACE(
@@ -684,6 +690,58 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
return RESULT_SUCCESS;
}
+// Wait for an address (via Address Arbiter)
+static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) {
+ NGLOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}",
+ address, type, value, timeout);
+ // If the passed address is a kernel virtual address, return invalid memory state.
+ if (Memory::IsKernelVirtualAddress(address)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+ // If the address is not properly aligned to 4 bytes, return invalid address.
+ if (address % sizeof(u32) != 0) {
+ return ERR_INVALID_ADDRESS;
+ }
+
+ switch (static_cast<AddressArbiter::ArbitrationType>(type)) {
+ case AddressArbiter::ArbitrationType::WaitIfLessThan:
+ return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false);
+ case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan:
+ return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, true);
+ case AddressArbiter::ArbitrationType::WaitIfEqual:
+ return AddressArbiter::WaitForAddressIfEqual(address, value, timeout);
+ default:
+ return ERR_INVALID_ENUM_VALUE;
+ }
+}
+
+// Signals to an address (via Address Arbiter)
+static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) {
+ NGLOG_WARNING(Kernel_SVC,
+ "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", address,
+ type, value, num_to_wake);
+ // If the passed address is a kernel virtual address, return invalid memory state.
+ if (Memory::IsKernelVirtualAddress(address)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+ // If the address is not properly aligned to 4 bytes, return invalid address.
+ if (address % sizeof(u32) != 0) {
+ return ERR_INVALID_ADDRESS;
+ }
+
+ switch (static_cast<AddressArbiter::SignalType>(type)) {
+ case AddressArbiter::SignalType::Signal:
+ return AddressArbiter::SignalToAddress(address, num_to_wake);
+ case AddressArbiter::SignalType::IncrementAndSignalIfEqual:
+ return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
+ case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual:
+ return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value,
+ num_to_wake);
+ default:
+ return ERR_INVALID_ENUM_VALUE;
+ }
+}
+
/// This returns the total CPU ticks elapsed since the CPU was powered-on
static u64 GetSystemTick() {
const u64 result{CoreTiming::GetTicks()};
@@ -744,7 +802,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
ASSERT(thread->owner_process->ideal_processor != THREADPROCESSORID_DEFAULT);
// Set the target CPU to the one specified in the process' exheader.
core = thread->owner_process->ideal_processor;
- mask = 1 << core;
+ mask = 1ull << core;
}
if (mask == 0) {
@@ -761,7 +819,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
}
// Error out if the input core isn't enabled in the input mask.
- if (core < Core::NUM_CPU_CORES && (mask & (1 << core)) == 0) {
+ if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
}
@@ -856,8 +914,8 @@ static const FunctionDef SVC_Table[] = {
{0x31, nullptr, "GetResourceLimitCurrentValue"},
{0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"},
{0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
- {0x34, nullptr, "WaitForAddress"},
- {0x35, nullptr, "SignalToAddress"},
+ {0x34, SvcWrap<WaitForAddress>, "WaitForAddress"},
+ {0x35, SvcWrap<SignalToAddress>, "SignalToAddress"},
{0x36, nullptr, "Unknown"},
{0x37, nullptr, "Unknown"},
{0x38, nullptr, "Unknown"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 40aa88cc1..79c3fe31b 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -179,6 +179,20 @@ void SvcWrap() {
FuncReturn(retval);
}
+template <ResultCode func(u64, u32, s32, s64)>
+void SvcWrap() {
+ FuncReturn(
+ func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3))
+ .raw);
+}
+
+template <ResultCode func(u64, u32, s32, s32)>
+void SvcWrap() {
+ FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF),
+ (s32)(PARAM(3) & 0xFFFFFFFF))
+ .raw);
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index cffa7ca83..2f333ec34 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -140,6 +140,11 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
}
}
+ if (thread->arb_wait_address != 0) {
+ ASSERT(thread->status == THREADSTATUS_WAIT_ARB);
+ thread->arb_wait_address = 0;
+ }
+
if (resume)
thread->ResumeFromWait();
}
@@ -179,6 +184,7 @@ void Thread::ResumeFromWait() {
case THREADSTATUS_WAIT_SLEEP:
case THREADSTATUS_WAIT_IPC:
case THREADSTATUS_WAIT_MUTEX:
+ case THREADSTATUS_WAIT_ARB:
break;
case THREADSTATUS_READY:
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 1d2da6d50..f1e759802 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -45,6 +45,7 @@ enum ThreadStatus {
THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
THREADSTATUS_WAIT_MUTEX, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
+ THREADSTATUS_WAIT_ARB, ///< Waiting due to a SignalToAddress/WaitForAddress svc
THREADSTATUS_DORMANT, ///< Created but not yet made ready
THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
};
@@ -230,6 +231,9 @@ public:
VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
Handle wait_handle; ///< The handle used to wait for the mutex.
+ // If waiting for an AddressArbiter, this is the address being waited on.
+ VAddr arb_wait_address{0};
+
std::string name;
/// Handle used by guest emulated application to access this thread
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 12954556d..b8d6b8d4d 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -561,7 +561,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{32, nullptr, "BeginBlockingHomeButton"},
{33, nullptr, "EndBlockingHomeButton"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
- {50, nullptr, "GetPseudoDeviceId"},
+ {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
{65, nullptr, "IsGamePlayRecordingSupported"},
{66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"},
@@ -684,6 +684,17 @@ void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_AM, "(STUBBED) called");
}
+void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+
+ // Returns a 128-bit UUID
+ rb.Push<u64>(0);
+ rb.Push<u64>(0);
+
+ NGLOG_WARNING(Service_AM, "(STUBBED) called");
+}
+
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 301a6c798..1da79fd01 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -138,6 +138,7 @@ private:
void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx);
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
void NotifyRunning(Kernel::HLERequestContext& ctx);
+ void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
};
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6e8002bc9..44b7ef216 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -57,27 +57,26 @@ private:
}
void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
- NGLOG_DEBUG(Service_Audio, "{}", ctx.Description());
- AudioRendererResponseData response_data{};
-
- response_data.section_0_size =
- static_cast<u32>(response_data.state_entries.size() * sizeof(AudioRendererStateEntry));
- response_data.section_1_size = static_cast<u32>(response_data.section_1.size());
- response_data.section_2_size = static_cast<u32>(response_data.section_2.size());
- response_data.section_3_size = static_cast<u32>(response_data.section_3.size());
- response_data.section_4_size = static_cast<u32>(response_data.section_4.size());
- response_data.section_5_size = static_cast<u32>(response_data.section_5.size());
- response_data.total_size = sizeof(AudioRendererResponseData);
-
- for (unsigned i = 0; i < response_data.state_entries.size(); i++) {
- // 4 = Busy and 5 = Ready?
- response_data.state_entries[i].state = 5;
+ AudioRendererConfig config;
+ auto buf = ctx.ReadBuffer();
+ std::memcpy(&config, buf.data(), sizeof(AudioRendererConfig));
+
+ AudioRendererResponse response_data{config};
+
+ ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
+
+ std::vector<u8> output(response_data.total_size);
+ std::memcpy(output.data(), &response_data, sizeof(AudioRendererResponse));
+ std::vector<MemoryPoolEntry> memory_pool(config.memory_pools_size / 0x20);
+ for (auto& entry : memory_pool) {
+ entry.state = 5;
}
+ std::memcpy(output.data() + sizeof(AudioRendererResponse), memory_pool.data(),
+ response_data.memory_pools_size);
- ctx.WriteBuffer(&response_data, response_data.total_size);
+ ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 2};
-
rb.Push(RESULT_SUCCESS);
NGLOG_WARNING(Service_Audio, "(STUBBED) called");
@@ -109,43 +108,55 @@ private:
NGLOG_WARNING(Service_Audio, "(STUBBED) called");
}
- struct AudioRendererStateEntry {
+ struct MemoryPoolEntry {
u32_le state;
u32_le unknown_4;
u32_le unknown_8;
u32_le unknown_c;
};
- static_assert(sizeof(AudioRendererStateEntry) == 0x10,
- "AudioRendererStateEntry has wrong size");
-
- struct AudioRendererResponseData {
- u32_le unknown_0;
- u32_le section_5_size;
- u32_le section_0_size;
- u32_le section_1_size;
+ static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
+
+ struct AudioRendererConfig {
+ u32 revision;
+ u32 behavior_size;
+ u32 memory_pools_size;
+ u32 voices_size;
+ u32 voice_resource_size;
+ u32 effects_size;
+ u32 mixes_size;
+ u32 sinks_size;
+ u32 performance_buffer_size;
+ INSERT_PADDING_WORDS(6);
+ u32 total_size;
+ };
+ static_assert(sizeof(AudioRendererConfig) == 0x40, "AudioRendererConfig has wrong size");
+
+ struct AudioRendererResponse {
+ AudioRendererResponse(const AudioRendererConfig& config) {
+ revision = config.revision;
+ error_info_size = 0xb0;
+ memory_pools_size = (config.memory_pools_size / 0x20) * 0x10;
+ voices_size = (config.voices_size / 0x170) * 0x10;
+ effects_size = (config.effects_size / 0xC0) * 0x10;
+ sinks_size = (config.sinks_size / 0x140) * 0x20;
+ performance_manager_size = 0x10;
+ total_size = sizeof(AudioRendererResponse) + error_info_size + memory_pools_size +
+ voices_size + effects_size + sinks_size + performance_manager_size;
+ }
+
+ u32_le revision;
+ u32_le error_info_size;
+ u32_le memory_pools_size;
+ u32_le voices_size;
u32_le unknown_10;
- u32_le section_2_size;
+ u32_le effects_size;
u32_le unknown_18;
- u32_le section_3_size;
- u32_le section_4_size;
- u32_le unknown_24;
- u32_le unknown_28;
- u32_le unknown_2c;
- u32_le unknown_30;
- u32_le unknown_34;
- u32_le unknown_38;
+ u32_le sinks_size;
+ u32_le performance_manager_size;
+ INSERT_PADDING_WORDS(6);
u32_le total_size;
-
- std::array<AudioRendererStateEntry, 0x18e> state_entries;
-
- std::array<u8, 0x600> section_1;
- std::array<u8, 0xe0> section_2;
- std::array<u8, 0x20> section_3;
- std::array<u8, 0x10> section_4;
- std::array<u8, 0xb0> section_5;
};
- static_assert(sizeof(AudioRendererResponseData) == 0x20e0,
- "AudioRendererResponseData has wrong size");
+ static_assert(sizeof(AudioRendererResponse) == 0x40, "AudioRendererResponse has wrong size");
/// This is used to trigger the audio event callback.
CoreTiming::EventType* audio_event;
@@ -258,7 +269,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto params = rp.PopRaw<WorkerBufferParameters>();
+ auto params = rp.PopRaw<AudioRendererParameters>();
u64 buffer_sz = Common::AlignUp(4 * params.unknown8, 0x40);
buffer_sz += params.unknownC * 1024;
@@ -328,7 +339,7 @@ bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
switch (feature) {
case AudioFeatures::Splitter:
- return version_num >= 2;
+ return version_num >= 2u;
default:
return false;
}
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index fe53de4ce..7dbd9b74d 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -22,7 +22,7 @@ private:
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
void GetAudioDevice(Kernel::HLERequestContext& ctx);
- struct WorkerBufferParameters {
+ struct AudioRendererParameters {
u32_le sample_rate;
u32_le sample_count;
u32_le unknown8;
@@ -38,8 +38,8 @@ private:
u8 padding2[4];
u32_le magic;
};
- static_assert(sizeof(WorkerBufferParameters) == 52,
- "WorkerBufferParameters is an invalid size");
+ static_assert(sizeof(AudioRendererParameters) == 52,
+ "AudioRendererParameters is an invalid size");
enum class AudioFeatures : u32 {
Splitter,
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 8a47bb7af..1cf97e876 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -4,6 +4,7 @@
#include <cinttypes>
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/filesystem.h"
@@ -258,9 +259,7 @@ public:
IPC::RequestParser rp{ctx};
auto file_buffer = ctx.ReadBuffer();
- auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
-
- std::string name(file_buffer.begin(), end);
+ std::string name = Common::StringFromBuffer(file_buffer);
u64 mode = rp.Pop<u64>();
u32 size = rp.Pop<u32>();
@@ -275,9 +274,7 @@ public:
IPC::RequestParser rp{ctx};
auto file_buffer = ctx.ReadBuffer();
- auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
-
- std::string name(file_buffer.begin(), end);
+ std::string name = Common::StringFromBuffer(file_buffer);
NGLOG_DEBUG(Service_FS, "called file {}", name);
@@ -289,9 +286,7 @@ public:
IPC::RequestParser rp{ctx};
auto file_buffer = ctx.ReadBuffer();
- auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
-
- std::string name(file_buffer.begin(), end);
+ std::string name = Common::StringFromBuffer(file_buffer);
NGLOG_DEBUG(Service_FS, "called directory {}", name);
@@ -305,13 +300,11 @@ public:
std::vector<u8> buffer;
buffer.resize(ctx.BufferDescriptorX()[0].Size());
Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
- auto end = std::find(buffer.begin(), buffer.end(), '\0');
- std::string src_name(buffer.begin(), end);
+ std::string src_name = Common::StringFromBuffer(buffer);
buffer.resize(ctx.BufferDescriptorX()[1].Size());
Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
- end = std::find(buffer.begin(), buffer.end(), '\0');
- std::string dst_name(buffer.begin(), end);
+ std::string dst_name = Common::StringFromBuffer(buffer);
NGLOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
@@ -323,9 +316,7 @@ public:
IPC::RequestParser rp{ctx};
auto file_buffer = ctx.ReadBuffer();
- auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
-
- std::string name(file_buffer.begin(), end);
+ std::string name = Common::StringFromBuffer(file_buffer);
auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
@@ -349,9 +340,7 @@ public:
IPC::RequestParser rp{ctx};
auto file_buffer = ctx.ReadBuffer();
- auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
-
- std::string name(file_buffer.begin(), end);
+ std::string name = Common::StringFromBuffer(file_buffer);
// TODO(Subv): Implement this filter.
u32 filter_flags = rp.Pop<u32>();
@@ -376,9 +365,7 @@ public:
IPC::RequestParser rp{ctx};
auto file_buffer = ctx.ReadBuffer();
- auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
-
- std::string name(file_buffer.begin(), end);
+ std::string name = Common::StringFromBuffer(file_buffer);
NGLOG_DEBUG(Service_FS, "called file {}", name);
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 00c5308ba..2696a8bf0 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -84,6 +84,10 @@ private:
for (size_t controller = 0; controller < mem.controllers.size(); controller++) {
for (int index = 0; index < HID_NUM_LAYOUTS; index++) {
+ // TODO(DarkLordZach): Is this layout/controller config actually invalid?
+ if (controller == Controller_Handheld && index == Layout_Single)
+ continue;
+
ControllerLayout& layout = mem.controllers[controller].layouts[index];
layout.header.num_entries = HID_NUM_ENTRIES;
layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
@@ -94,7 +98,6 @@ private:
layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
- entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
entry.timestamp++;
// TODO(shinyquagsire23): Is this always identical to timestamp?
entry.timestamp_2++;
@@ -103,6 +106,8 @@ private:
if (controller != Controller_Handheld)
continue;
+ entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
+
// TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future?
// For now everything is just the default handheld layout, but split Joy-Con will
// rotate the face buttons and directions for certain layouts.
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 15eee8f01..b499308d6 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -12,7 +12,7 @@ namespace Service::HID {
// Begin enums and output structs
constexpr u32 HID_NUM_ENTRIES = 17;
-constexpr u32 HID_NUM_LAYOUTS = 2;
+constexpr u32 HID_NUM_LAYOUTS = 7;
constexpr s32 HID_JOYSTICK_MAX = 0x8000;
constexpr s32 HID_JOYSTICK_MIN = -0x8000;
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
new file mode 100644
index 000000000..b3a85b818
--- /dev/null
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -0,0 +1,50 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/client_session.h"
+#include "core/hle/service/mm/mm_u.h"
+
+namespace Service::MM {
+
+void InstallInterfaces(SM::ServiceManager& service_manager) {
+ std::make_shared<MM_U>()->InstallAsService(service_manager);
+}
+
+void MM_U::Initialize(Kernel::HLERequestContext& ctx) {
+ NGLOG_WARNING(Service_MM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void MM_U::SetAndWait(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ min = rp.Pop<u32>();
+ max = rp.Pop<u32>();
+ current = min;
+
+ NGLOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void MM_U::Get(Kernel::HLERequestContext& ctx) {
+ NGLOG_WARNING(Service_MM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(current);
+}
+
+MM_U::MM_U() : ServiceFramework("mm:u") {
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "InitializeOld"}, {1, nullptr, "FinalizeOld"},
+ {2, nullptr, "SetAndWaitOld"}, {3, nullptr, "GetOld"},
+ {4, &MM_U::Initialize, "Initialize"}, {5, nullptr, "Finalize"},
+ {6, &MM_U::SetAndWait, "SetAndWait"}, {7, &MM_U::Get, "Get"},
+ };
+ RegisterHandlers(functions);
+}
+
+} // namespace Service::MM
diff --git a/src/core/hle/service/mm/mm_u.h b/src/core/hle/service/mm/mm_u.h
new file mode 100644
index 000000000..79eeedf9c
--- /dev/null
+++ b/src/core/hle/service/mm/mm_u.h
@@ -0,0 +1,29 @@
+// 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::MM {
+
+class MM_U final : public ServiceFramework<MM_U> {
+public:
+ MM_U();
+ ~MM_U() = default;
+
+private:
+ void Initialize(Kernel::HLERequestContext& ctx);
+ void SetAndWait(Kernel::HLERequestContext& ctx);
+ void Get(Kernel::HLERequestContext& ctx);
+
+ u32 min{0};
+ u32 max{0};
+ u32 current{0};
+};
+
+/// Registers all MM services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& service_manager);
+
+} // namespace Service::MM
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 2af4465de..2a9f84037 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -4,6 +4,8 @@
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/service/hid/hid.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_user.h"
@@ -18,7 +20,7 @@ public:
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "Initialize"},
{1, nullptr, "Finalize"},
- {2, nullptr, "ListDevices"},
+ {2, &IUser::ListDevices, "ListDevices"},
{3, nullptr, "StartDetection"},
{4, nullptr, "StopDetection"},
{5, nullptr, "Mount"},
@@ -33,24 +35,116 @@ public:
{14, nullptr, "GetRegisterInfo"},
{15, nullptr, "GetCommonInfo"},
{16, nullptr, "GetModelInfo"},
- {17, nullptr, "AttachActivateEvent"},
- {18, nullptr, "AttachDeactivateEvent"},
- {19, nullptr, "GetState"},
- {20, nullptr, "GetDeviceState"},
- {21, nullptr, "GetNpadId"},
+ {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
+ {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
+ {19, &IUser::GetState, "GetState"},
+ {20, &IUser::GetDeviceState, "GetDeviceState"},
+ {21, &IUser::GetNpadId, "GetNpadId"},
{22, nullptr, "GetApplicationArea2"},
- {23, nullptr, "AttachAvailabilityChangeEvent"},
+ {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{24, nullptr, "RecreateApplicationArea"},
};
RegisterHandlers(functions);
+
+ activate_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:ActivateEvent");
+ deactivate_event =
+ Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
+ availability_change_event =
+ Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent");
}
private:
+ enum class State : u32 {
+ NonInitialized = 0,
+ Initialized = 1,
+ };
+
+ enum class DeviceState : u32 {
+ Initialized = 0,
+ };
+
void Initialize(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_NFP, "(STUBBED) called");
+
+ state = State::Initialized;
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+
+ void ListDevices(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u32 array_size = rp.Pop<u32>();
+
+ ctx.WriteBuffer(&device_handle, sizeof(device_handle));
+
+ NGLOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0);
+ }
+
+ void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 dev_handle = rp.Pop<u64>();
+ NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(activate_event);
+ }
+
+ void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 dev_handle = rp.Pop<u64>();
+ NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(deactivate_event);
+ }
+
+ void GetState(Kernel::HLERequestContext& ctx) {
+ NGLOG_WARNING(Service_NFP, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(static_cast<u32>(state));
+ }
+
+ void GetDeviceState(Kernel::HLERequestContext& ctx) {
+ NGLOG_WARNING(Service_NFP, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(static_cast<u32>(device_state));
+ }
+
+ void GetNpadId(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 dev_handle = rp.Pop<u64>();
+ NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(npad_id);
+ }
+
+ void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 dev_handle = rp.Pop<u64>();
+ NGLOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(availability_change_event);
+ }
+
+ const u64 device_handle{0xDEAD};
+ const HID::ControllerID npad_id{HID::Controller_Player1};
+ State state{State::NonInitialized};
+ DeviceState device_state{DeviceState::Initialized};
+ Kernel::SharedPtr<Kernel::Event> activate_event;
+ Kernel::SharedPtr<Kernel::Event> deactivate_event;
+ Kernel::SharedPtr<Kernel::Event> availability_change_event;
};
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index eee92cfcd..62489c7fe 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -38,7 +38,7 @@ public:
{8, nullptr, "SetPriority"},
{9, nullptr, "SetNetworkProfileId"},
{10, nullptr, "SetRejectable"},
- {11, nullptr, "SetConnectionConfirmationOption"},
+ {11, &IRequest::SetConnectionConfirmationOption, "SetConnectionConfirmationOption"},
{12, nullptr, "SetPersistent"},
{13, nullptr, "SetInstant"},
{14, nullptr, "SetSustainable"},
@@ -67,23 +67,32 @@ private:
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
+
void GetResult(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+
void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 2};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event1, event2);
}
+
void Cancel(Kernel::HLERequestContext& ctx) {
NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+ void SetConnectionConfirmationOption(Kernel::HLERequestContext& ctx) {
+ NGLOG_WARNING(Service_NIFM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
Kernel::SharedPtr<Kernel::Event> event1, event2;
};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index a9538ff43..0abc0de83 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -26,6 +26,10 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
return ZCullGetInfo(input, output);
case IoctlCommand::IocZbcSetTable:
return ZBCSetTable(input, output);
+ case IoctlCommand::IocZbcQueryTable:
+ return ZBCQueryTable(input, output);
+ case IoctlCommand::IocFlushL2:
+ return FlushL2(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
@@ -136,4 +140,22 @@ u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>&
return 0;
}
+u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
+ NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
+ IoctlZbcQueryTable params{};
+ std::memcpy(&params, input.data(), input.size());
+ // TODO : To implement properly
+ std::memcpy(output.data(), &params, output.size());
+ return 0;
+}
+
+u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
+ NGLOG_WARNING(Service_NVDRV, "(STUBBED) called");
+ IoctlFlushL2 params{};
+ std::memcpy(&params, input.data(), input.size());
+ // TODO : To implement properly
+ std::memcpy(output.data(), &params, output.size());
+ return 0;
+}
+
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 1d5ba2e67..f09113e67 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -26,6 +26,18 @@ private:
IocZcullGetCtxSizeCommand = 0x80044701,
IocZcullGetInfo = 0x80284702,
IocZbcSetTable = 0x402C4703,
+ IocZbcQueryTable = 0xC0344704,
+ IocFlushL2 = 0x40084707,
+ IocInvalICache = 0x4008470D,
+ IocSetMmudebugMode = 0x4008470E,
+ IocSetSmDebugMode = 0x4010470F,
+ IocWaitForPause = 0xC0084710,
+ IocGetTcpExceptionEnStatus = 0x80084711,
+ IocNumVsms = 0x80084712,
+ IocVsmsMapping = 0xC0044713,
+ IocGetErrorChannelUserData = 0xC008471B,
+ IocGetGpuTime = 0xC010471C,
+ IocGetCpuTimeCorrelationInfo = 0xC108471D,
};
struct IoctlGpuCharacteristics {
@@ -127,12 +139,31 @@ private:
};
static_assert(sizeof(IoctlZbcSetTable) == 44, "IoctlZbcSetTable is incorrect size");
+ struct IoctlZbcQueryTable {
+ u32_le color_ds[4];
+ u32_le color_l2[4];
+ u32_le depth;
+ u32_le ref_cnt;
+ u32_le format;
+ u32_le type;
+ u32_le index_size;
+ };
+ static_assert(sizeof(IoctlZbcQueryTable) == 52, "IoctlZbcQueryTable is incorrect size");
+
+ struct IoctlFlushL2 {
+ u32_le flush; // l2_flush | l2_invalidate << 1 | fb_flush << 2
+ u32_le reserved;
+ };
+ static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size");
+
u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
+ u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
+ u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 79aab87f9..ed7b6dc03 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -121,8 +121,9 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
}
u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
- if (input.size() < sizeof(IoctlSubmitGpfifo))
+ if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
+ }
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 409fec470..bdd9eb5a5 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -26,6 +26,7 @@
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/lm/lm.h"
+#include "core/hle/service/mm/mm_u.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/ns/ns.h"
@@ -191,6 +192,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
Friend::InstallInterfaces(*sm);
HID::InstallInterfaces(*sm);
LM::InstallInterfaces(*sm);
+ MM::InstallInterfaces(*sm);
NFP::InstallInterfaces(*sm);
NIFM::InstallInterfaces(*sm);
NS::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index f0572bed6..baeecb0ec 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -12,9 +12,6 @@
namespace Service::Set {
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- u32 id = rp.Pop<u32>();
-
static constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
@@ -50,7 +47,7 @@ SET::SET() : ServiceFramework("set") {
{2, nullptr, "MakeLanguageCode"},
{3, nullptr, "GetAvailableLanguageCodeCount"},
{4, nullptr, "GetRegionCode"},
- {5, nullptr, "GetAvailableLanguageCodes2"},
+ {5, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes2"},
{6, nullptr, "GetAvailableLanguageCodeCount2"},
{7, nullptr, "GetKeyCodeMap"},
{8, nullptr, "GetQuestFlag"},