aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/function_wrappers.h79
-rw-r--r--src/core/hle/hle.cpp6
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp67
-rw-r--r--src/core/hle/kernel/address_arbiter.h28
-rw-r--r--src/core/hle/kernel/event.cpp146
-rw-r--r--src/core/hle/kernel/event.h64
-rw-r--r--src/core/hle/kernel/kernel.cpp45
-rw-r--r--src/core/hle/kernel/kernel.h108
-rw-r--r--src/core/hle/kernel/mutex.cpp192
-rw-r--r--src/core/hle/kernel/mutex.h55
-rw-r--r--src/core/hle/kernel/semaphore.cpp81
-rw-r--r--src/core/hle/kernel/semaphore.h58
-rw-r--r--src/core/hle/kernel/session.cpp13
-rw-r--r--src/core/hle/kernel/session.h17
-rw-r--r--src/core/hle/kernel/shared_memory.cpp70
-rw-r--r--src/core/hle/kernel/shared_memory.h61
-rw-r--r--src/core/hle/kernel/thread.cpp226
-rw-r--r--src/core/hle/kernel/thread.h98
-rw-r--r--src/core/hle/kernel/timer.cpp124
-rw-r--r--src/core/hle/kernel/timer.h73
-rw-r--r--src/core/hle/result.h31
-rw-r--r--src/core/hle/service/ac_u.cpp2
-rw-r--r--src/core/hle/service/act_u.cpp2
-rw-r--r--src/core/hle/service/am_app.cpp2
-rw-r--r--src/core/hle/service/am_net.cpp2
-rw-r--r--src/core/hle/service/am_sys.cpp24
-rw-r--r--src/core/hle/service/am_sys.h23
-rw-r--r--src/core/hle/service/apt_a.cpp4
-rw-r--r--src/core/hle/service/apt_s.cpp123
-rw-r--r--src/core/hle/service/apt_s.h30
-rw-r--r--src/core/hle/service/apt_u.cpp421
-rw-r--r--src/core/hle/service/boss_p.cpp24
-rw-r--r--src/core/hle/service/boss_p.h23
-rw-r--r--src/core/hle/service/boss_u.cpp2
-rw-r--r--src/core/hle/service/cam_u.cpp24
-rw-r--r--src/core/hle/service/cam_u.h23
-rw-r--r--src/core/hle/service/cecd_s.cpp24
-rw-r--r--src/core/hle/service/cecd_s.h23
-rw-r--r--src/core/hle/service/cecd_u.cpp2
-rw-r--r--src/core/hle/service/cfg/cfg.h2
-rw-r--r--src/core/hle/service/cfg/cfg_i.cpp2
-rw-r--r--src/core/hle/service/cfg/cfg_s.cpp98
-rw-r--r--src/core/hle/service/cfg/cfg_s.h23
-rw-r--r--src/core/hle/service/cfg/cfg_u.cpp2
-rw-r--r--src/core/hle/service/csnd_snd.cpp2
-rw-r--r--src/core/hle/service/dsp_dsp.cpp32
-rw-r--r--src/core/hle/service/err_f.cpp2
-rw-r--r--src/core/hle/service/frd_a.cpp24
-rw-r--r--src/core/hle/service/frd_a.h23
-rw-r--r--src/core/hle/service/frd_u.cpp2
-rw-r--r--src/core/hle/service/fs/archive.cpp73
-rw-r--r--src/core/hle/service/fs/archive.h24
-rw-r--r--src/core/hle/service/fs/fs_user.cpp40
-rw-r--r--src/core/hle/service/gsp_gpu.cpp68
-rw-r--r--src/core/hle/service/gsp_gpu.h25
-rw-r--r--src/core/hle/service/gsp_lcd.cpp26
-rw-r--r--src/core/hle/service/gsp_lcd.h24
-rw-r--r--src/core/hle/service/hid/hid.cpp142
-rw-r--r--src/core/hle/service/hid/hid.h (renamed from src/core/hle/service/hid_user.h)42
-rw-r--r--src/core/hle/service/hid/hid_spvr.cpp38
-rw-r--r--src/core/hle/service/hid/hid_spvr.h23
-rw-r--r--src/core/hle/service/hid/hid_user.cpp78
-rw-r--r--src/core/hle/service/hid/hid_user.h29
-rw-r--r--src/core/hle/service/hid_user.cpp197
-rw-r--r--src/core/hle/service/http_c.cpp2
-rw-r--r--src/core/hle/service/ir_rst.cpp2
-rw-r--r--src/core/hle/service/ir_u.cpp2
-rw-r--r--src/core/hle/service/ldr_ro.cpp2
-rw-r--r--src/core/hle/service/mic_u.cpp2
-rw-r--r--src/core/hle/service/ndm_u.cpp2
-rw-r--r--src/core/hle/service/news_s.cpp25
-rw-r--r--src/core/hle/service/news_s.h23
-rw-r--r--src/core/hle/service/news_u.cpp2
-rw-r--r--src/core/hle/service/nim_aoc.cpp2
-rw-r--r--src/core/hle/service/ns_s.cpp27
-rw-r--r--src/core/hle/service/ns_s.h24
-rw-r--r--src/core/hle/service/nwm_uds.cpp2
-rw-r--r--src/core/hle/service/pm_app.cpp2
-rw-r--r--src/core/hle/service/ptm_play.cpp28
-rw-r--r--src/core/hle/service/ptm_play.h23
-rw-r--r--src/core/hle/service/ptm_sysm.cpp56
-rw-r--r--src/core/hle/service/ptm_sysm.h23
-rw-r--r--src/core/hle/service/ptm_u.cpp2
-rw-r--r--src/core/hle/service/service.cpp132
-rw-r--r--src/core/hle/service/service.h73
-rw-r--r--src/core/hle/service/soc_u.cpp15
-rw-r--r--src/core/hle/service/srv.cpp16
-rw-r--r--src/core/hle/service/ssl_c.cpp2
-rw-r--r--src/core/hle/service/y2r_u.cpp2
-rw-r--r--src/core/hle/shared_page.cpp80
-rw-r--r--src/core/hle/shared_page.h26
-rw-r--r--src/core/hle/svc.cpp480
92 files changed, 2960 insertions, 1578 deletions
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index a2f51b41b..0b6b6f518 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -33,114 +33,105 @@ static inline void FuncReturn64(u64 res) {
}
////////////////////////////////////////////////////////////////////////////////////////////////////
-// Function wrappers that return type s32
+// Function wrappers that return type ResultCode
-template<s32 func(u32, u32, u32, u32)> void Wrap() {
- FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)));
+template<ResultCode func(u32, u32, u32, u32)> void Wrap() {
+ FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)).raw);
}
-template<s32 func(u32, u32, u32, u32, u32)> void Wrap() {
- FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)));
-}
-
-template<s32 func(u32*, u32, u32, u32, u32, u32)> void Wrap(){
+template<ResultCode func(u32*, u32, u32, u32, u32, u32)> void Wrap(){
u32 param_1 = 0;
- u32 retval = func(&param_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
+ u32 retval = func(&param_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw;
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
-template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() {
+template<ResultCode func(s32*, u32*, s32, bool, s64)> void Wrap() {
s32 param_1 = 0;
s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
- (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0)));
+ (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))).raw;
Core::g_app_core->SetReg(1, (u32)param_1);
FuncReturn(retval);
}
-// TODO(bunnei): Is this correct? Probably not - Last parameter looks wrong for ArbitrateAddress
-template<s32 func(u32, u32, u32, u32, s64)> void Wrap() {
- FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))));
+template<ResultCode func(u32, u32, u32, u32, s64)> void Wrap() {
+ FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))).raw);
}
-template<s32 func(u32*)> void Wrap(){
+template<ResultCode func(u32*)> void Wrap(){
u32 param_1 = 0;
- u32 retval = func(&param_1);
+ u32 retval = func(&param_1).raw;
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
-template<s32 func(u32, s64)> void Wrap() {
- FuncReturn(func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2))));
+template<ResultCode func(u32, s64)> void Wrap() {
+ FuncReturn(func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2))).raw);
}
-template<s32 func(void*, void*, u32)> void Wrap(){
- FuncReturn(func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2)));
+template<ResultCode func(void*, void*, u32)> void Wrap(){
+ FuncReturn(func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2)).raw);
}
-template<s32 func(s32*, u32)> void Wrap(){
+template<ResultCode func(s32*, u32)> void Wrap(){
s32 param_1 = 0;
- u32 retval = func(&param_1, PARAM(1));
+ u32 retval = func(&param_1, PARAM(1)).raw;
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
-template<s32 func(u32, s32)> void Wrap() {
- FuncReturn(func(PARAM(0), (s32)PARAM(1)));
+template<ResultCode func(u32, s32)> void Wrap() {
+ FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw);
}
-template<s32 func(u32*, u32)> void Wrap(){
+template<ResultCode func(u32*, u32)> void Wrap(){
u32 param_1 = 0;
- u32 retval = func(&param_1, PARAM(1));
+ u32 retval = func(&param_1, PARAM(1)).raw;
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
-template<s32 func(u32)> void Wrap() {
- FuncReturn(func(PARAM(0)));
-}
-
-template<s32 func(void*)> void Wrap() {
- FuncReturn(func(Memory::GetPointer(PARAM(0))));
+template<ResultCode func(u32)> void Wrap() {
+ FuncReturn(func(PARAM(0)).raw);
}
-template<s32 func(s64*, u32, void*, s32)> void Wrap(){
+template<ResultCode func(s64*, u32, void*, s32)> void Wrap(){
FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)),
- (s32)PARAM(3)));
+ (s32)PARAM(3)).raw);
}
-template<s32 func(u32*, const char*)> void Wrap() {
+template<ResultCode func(u32*, const char*)> void Wrap() {
u32 param_1 = 0;
- u32 retval = func(&param_1, Memory::GetCharPointer(PARAM(1)));
+ u32 retval = func(&param_1, Memory::GetCharPointer(PARAM(1))).raw;
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
-template<s32 func(u32*, s32, s32)> void Wrap() {
+template<ResultCode func(u32*, s32, s32)> void Wrap() {
u32 param_1 = 0;
- u32 retval = func(&param_1, PARAM(1), PARAM(2));
+ u32 retval = func(&param_1, PARAM(1), PARAM(2)).raw;
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
-template<s32 func(s32*, u32, s32)> void Wrap() {
+template<ResultCode func(s32*, u32, s32)> void Wrap() {
s32 param_1 = 0;
- u32 retval = func(&param_1, PARAM(1), PARAM(2));
+ u32 retval = func(&param_1, PARAM(1), PARAM(2)).raw;
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
-template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() {
+template<ResultCode func(u32*, u32, u32, u32, u32)> void Wrap() {
u32 param_1 = 0;
- u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4));
+ u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw;
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
-template<s32 func(u32, s64, s64)> void Wrap() {
+template<ResultCode func(u32, s64, s64)> void Wrap() {
s64 param1 = ((u64)PARAM(3) << 32) | PARAM(2);
s64 param2 = ((u64)PARAM(4) << 32) | PARAM(1);
- FuncReturn(func(PARAM(0), param1, param2));
+ FuncReturn(func(PARAM(0), param1, param2).raw);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index 5d77a1458..5a2edeb4a 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -7,10 +7,12 @@
#include "core/arm/arm_interface.h"
#include "core/mem_map.h"
#include "core/hle/hle.h"
+#include "core/hle/shared_page.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/cfg/cfg.h"
+#include "core/hle/service/hid/hid.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -69,13 +71,17 @@ void Init() {
Service::Init();
Service::FS::ArchiveInit();
Service::CFG::CFGInit();
+ Service::HID::HIDInit();
RegisterAllModules();
+ SharedPage::Init();
+
LOG_DEBUG(Kernel, "initialized OK");
}
void Shutdown() {
+ Service::HID::HIDShutdown();
Service::CFG::CFGShutdown();
Service::FS::ArchiveShutdown();
Service::Shutdown();
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 62e3460e1..42f8ce2d9 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -15,53 +15,64 @@
namespace Kernel {
-class AddressArbiter : public Object {
-public:
- std::string GetTypeName() const override { return "Arbiter"; }
- std::string GetName() const override { return name; }
+AddressArbiter::AddressArbiter() {}
+AddressArbiter::~AddressArbiter() {}
- static const HandleType HANDLE_TYPE = HandleType::AddressArbiter;
- HandleType GetHandleType() const override { return HANDLE_TYPE; }
+SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) {
+ SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter);
- std::string name; ///< Name of address arbiter object (optional)
-};
+ address_arbiter->name = std::move(name);
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/// Arbitrate an address
-ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
- Object* object = Kernel::g_handle_table.GetGeneric(handle).get();
- if (object == nullptr)
- return InvalidHandle(ErrorModule::Kernel);
+ return address_arbiter;
+}
+ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value,
+ u64 nanoseconds) {
switch (type) {
// Signal thread(s) waiting for arbitrate address...
case ArbitrationType::Signal:
// Negative value means resume all threads
if (value < 0) {
- ArbitrateAllThreads(object, address);
+ ArbitrateAllThreads(address);
} else {
// Resume first N threads
for(int i = 0; i < value; i++)
- ArbitrateHighestPriorityThread(object, address);
+ ArbitrateHighestPriorityThread(address);
}
break;
// Wait current thread (acquire the arbiter)...
case ArbitrationType::WaitIfLessThan:
if ((s32)Memory::Read32(address) <= value) {
- Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address);
+ Kernel::WaitCurrentThread_ArbitrateAddress(address);
+ HLE::Reschedule(__func__);
+ }
+ break;
+ case ArbitrationType::WaitIfLessThanWithTimeout:
+ if ((s32)Memory::Read32(address) <= value) {
+ Kernel::WaitCurrentThread_ArbitrateAddress(address);
+ GetCurrentThread()->WakeAfterDelay(nanoseconds);
HLE::Reschedule(__func__);
}
break;
-
case ArbitrationType::DecrementAndWaitIfLessThan:
{
s32 memory_value = Memory::Read32(address) - 1;
Memory::Write32(address, memory_value);
if (memory_value <= value) {
- Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address);
+ Kernel::WaitCurrentThread_ArbitrateAddress(address);
+ HLE::Reschedule(__func__);
+ }
+ break;
+ }
+ case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout:
+ {
+ s32 memory_value = Memory::Read32(address) - 1;
+ Memory::Write32(address, memory_value);
+ if (memory_value <= value) {
+ Kernel::WaitCurrentThread_ArbitrateAddress(address);
+ GetCurrentThread()->WakeAfterDelay(nanoseconds);
HLE::Reschedule(__func__);
}
break;
@@ -74,20 +85,4 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3
return RESULT_SUCCESS;
}
-/// Create an address arbiter
-AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) {
- AddressArbiter* address_arbiter = new AddressArbiter;
- // TOOD(yuriks): Fix error reporting
- handle = Kernel::g_handle_table.Create(address_arbiter).ValueOr(INVALID_HANDLE);
- address_arbiter->name = name;
- return address_arbiter;
-}
-
-/// Create an address arbiter
-Handle CreateAddressArbiter(const std::string& name) {
- Handle handle;
- CreateAddressArbiter(handle, name);
- return handle;
-}
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index 030e7ad7b..8f6a1a8df 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -18,7 +18,6 @@
namespace Kernel {
-/// Address arbitration types
enum class ArbitrationType : u32 {
Signal,
WaitIfLessThan,
@@ -27,10 +26,29 @@ enum class ArbitrationType : u32 {
DecrementAndWaitIfLessThanWithTimeout,
};
-/// Arbitrate an address
-ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
+class AddressArbiter final : public Object {
+public:
+ /**
+ * Creates an address arbiter.
+ *
+ * @param name Optional name used for debugging.
+ * @returns The created AddressArbiter.
+ */
+ static SharedPtr<AddressArbiter> Create(std::string name = "Unknown");
-/// Create an address arbiter
-Handle CreateAddressArbiter(const std::string& name = "Unknown");
+ std::string GetTypeName() const override { return "Arbiter"; }
+ std::string GetName() const override { return name; }
+
+ static const HandleType HANDLE_TYPE = HandleType::AddressArbiter;
+ HandleType GetHandleType() const override { return HANDLE_TYPE; }
+
+ std::string name; ///< Name of address arbiter object (optional)
+
+ ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds);
+
+private:
+ AddressArbiter();
+ ~AddressArbiter() override;
+};
} // namespace FileSys
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 271190dbe..898e1c98f 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -14,144 +14,38 @@
namespace Kernel {
-class Event : public Object {
-public:
- std::string GetTypeName() const override { return "Event"; }
- std::string GetName() const override { return name; }
+Event::Event() {}
+Event::~Event() {}
- static const HandleType HANDLE_TYPE = HandleType::Event;
- HandleType GetHandleType() const override { return HANDLE_TYPE; }
+SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) {
+ SharedPtr<Event> evt(new Event);
- ResetType intitial_reset_type; ///< ResetType specified at Event initialization
- ResetType reset_type; ///< Current ResetType
-
- bool locked; ///< Event signal wait
- bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough)
- std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
- std::string name; ///< Name of event (optional)
-
- ResultVal<bool> WaitSynchronization() override {
- bool wait = locked;
- if (locked) {
- Handle thread = GetCurrentThread()->GetHandle();
- if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
- waiting_threads.push_back(thread);
- }
- Kernel::WaitCurrentThread(WAITTYPE_EVENT, this);
- }
- if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
- locked = true;
- }
- return MakeResult<bool>(wait);
- }
-};
-
-/**
- * Hackish function to set an events permanent lock state, used to pass through synch blocks
- * @param handle Handle to event to change
- * @param permanent_locked Boolean permanent locked value to set event
- * @return Result of operation, 0 on success, otherwise error code
- */
-ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
- Event* evt = g_handle_table.Get<Event>(handle).get();
- if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
-
- evt->permanent_locked = permanent_locked;
- return RESULT_SUCCESS;
-}
-
-/**
- * Changes whether an event is locked or not
- * @param handle Handle to event to change
- * @param locked Boolean locked value to set event
- * @return Result of operation, 0 on success, otherwise error code
- */
-ResultCode SetEventLocked(const Handle handle, const bool locked) {
- Event* evt = g_handle_table.Get<Event>(handle).get();
- if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
+ evt->signaled = false;
+ evt->reset_type = evt->intitial_reset_type = reset_type;
+ evt->name = std::move(name);
- if (!evt->permanent_locked) {
- evt->locked = locked;
- }
- return RESULT_SUCCESS;
+ return evt;
}
-/**
- * Signals an event
- * @param handle Handle to event to signal
- * @return Result of operation, 0 on success, otherwise error code
- */
-ResultCode SignalEvent(const Handle handle) {
- Event* evt = g_handle_table.Get<Event>(handle).get();
- if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
-
- // Resume threads waiting for event to signal
- bool event_caught = false;
- for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
- Thread* thread = Kernel::g_handle_table.Get<Thread>(evt->waiting_threads[i]).get();
- if (thread != nullptr)
- thread->ResumeFromWait();
-
- // If any thread is signalled awake by this event, assume the event was "caught" and reset
- // the event. This will result in the next thread waiting on the event to block. Otherwise,
- // the event will not be reset, and the next thread to call WaitSynchronization on it will
- // not block. Not sure if this is correct behavior, but it seems to work.
- event_caught = true;
- }
- evt->waiting_threads.clear();
-
- if (!evt->permanent_locked) {
- evt->locked = event_caught;
- }
- return RESULT_SUCCESS;
+bool Event::ShouldWait() {
+ return !signaled;
}
-/**
- * Clears an event
- * @param handle Handle to event to clear
- * @return Result of operation, 0 on success, otherwise error code
- */
-ResultCode ClearEvent(Handle handle) {
- Event* evt = g_handle_table.Get<Event>(handle).get();
- if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
+void Event::Acquire() {
+ _assert_msg_(Kernel, !ShouldWait(), "object unavailable!");
- if (!evt->permanent_locked) {
- evt->locked = true;
- }
- return RESULT_SUCCESS;
+ // Release the event if it's not sticky...
+ if (reset_type != RESETTYPE_STICKY)
+ signaled = false;
}
-/**
- * Creates an event
- * @param handle Reference to handle for the newly created mutex
- * @param reset_type ResetType describing how to create event
- * @param name Optional name of event
- * @return Newly created Event object
- */
-Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) {
- Event* evt = new Event;
-
- // TOOD(yuriks): Fix error reporting
- handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE);
-
- evt->locked = true;
- evt->permanent_locked = false;
- evt->reset_type = evt->intitial_reset_type = reset_type;
- evt->name = name;
-
- return evt;
+void Event::Signal() {
+ signaled = true;
+ WakeupAllWaitingThreads();
}
-/**
- * Creates an event
- * @param reset_type ResetType describing how to create event
- * @param name Optional name of event
- * @return Handle to newly created Event object
- */
-Handle CreateEvent(const ResetType reset_type, const std::string& name) {
- Handle handle;
- Event* evt = CreateEvent(handle, reset_type, name);
- return handle;
+void Event::Clear() {
+ signaled = false;
}
} // namespace
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index da793df1a..fba960d2a 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -11,38 +11,36 @@
namespace Kernel {
-/**
- * Changes whether an event is locked or not
- * @param handle Handle to event to change
- * @param locked Boolean locked value to set event
- */
-ResultCode SetEventLocked(const Handle handle, const bool locked);
-
-/**
- * Hackish function to set an events permanent lock state, used to pass through synch blocks
- * @param handle Handle to event to change
- * @param permanent_locked Boolean permanent locked value to set event
- */
-ResultCode SetPermanentLock(Handle handle, const bool permanent_locked);
-
-/**
- * Signals an event
- * @param handle Handle to event to signal
- */
-ResultCode SignalEvent(const Handle handle);
-
-/**
- * Clears an event
- * @param handle Handle to event to clear
- */
-ResultCode ClearEvent(Handle handle);
-
-/**
- * Creates an event
- * @param reset_type ResetType describing how to create event
- * @param name Optional name of event
- * @return Handle to newly created Event object
- */
-Handle CreateEvent(const ResetType reset_type, const std::string& name="Unknown");
+class Event final : public WaitObject {
+public:
+ /**
+ * Creates an event
+ * @param reset_type ResetType describing how to create event
+ * @param name Optional name of event
+ */
+ static SharedPtr<Event> Create(ResetType reset_type, std::string name = "Unknown");
+
+ std::string GetTypeName() const override { return "Event"; }
+ std::string GetName() const override { return name; }
+
+ static const HandleType HANDLE_TYPE = HandleType::Event;
+ HandleType GetHandleType() const override { return HANDLE_TYPE; }
+
+ ResetType intitial_reset_type; ///< ResetType specified at Event initialization
+ ResetType reset_type; ///< Current ResetType
+
+ bool signaled; ///< Whether the event has already been signaled
+ std::string name; ///< Name of event (optional)
+
+ bool ShouldWait() override;
+ void Acquire() override;
+
+ void Signal();
+ void Clear();
+
+private:
+ Event();
+ ~Event() override;
+};
} // namespace
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index d3684896f..52dca4dd8 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -14,10 +14,47 @@
namespace Kernel {
+unsigned int Object::next_object_id = 0;
+
SharedPtr<Thread> g_main_thread = nullptr;
HandleTable g_handle_table;
u64 g_program_id = 0;
+void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) {
+ auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
+ if (itr == waiting_threads.end())
+ waiting_threads.push_back(std::move(thread));
+}
+
+void WaitObject::RemoveWaitingThread(Thread* thread) {
+ auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
+ if (itr != waiting_threads.end())
+ waiting_threads.erase(itr);
+}
+
+SharedPtr<Thread> WaitObject::WakeupNextThread() {
+ if (waiting_threads.empty())
+ return nullptr;
+
+ auto next_thread = std::move(waiting_threads.front());
+ waiting_threads.erase(waiting_threads.begin());
+
+ next_thread->ReleaseWaitObject(this);
+
+ return next_thread;
+}
+
+void WaitObject::WakeupAllWaitingThreads() {
+ auto waiting_threads_copy = waiting_threads;
+
+ // We use a copy because ReleaseWaitObject will remove the thread from this object's
+ // waiting_threads list
+ for (auto thread : waiting_threads_copy)
+ thread->ReleaseWaitObject(this);
+
+ _assert_msg_(Kernel, waiting_threads.empty(), "failed to awaken all waiting threads!");
+}
+
HandleTable::HandleTable() {
next_generation = 1;
Clear();
@@ -39,13 +76,10 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
// CTR-OS doesn't use generation 0, so skip straight to 1.
if (next_generation >= (1 << 15)) next_generation = 1;
- Handle handle = generation | (slot << 15);
- if (obj->handle == INVALID_HANDLE)
- obj->handle = handle;
-
generations[slot] = generation;
objects[slot] = std::move(obj);
+ Handle handle = generation | (slot << 15);
return MakeResult<Handle>(handle);
}
@@ -63,11 +97,10 @@ ResultCode HandleTable::Close(Handle handle) {
return ERR_INVALID_HANDLE;
size_t slot = GetSlot(handle);
- u16 generation = GetGeneration(handle);
objects[slot] = nullptr;
- generations[generation] = next_free_slot;
+ generations[slot] = next_free_slot;
next_free_slot = slot;
return RESULT_SUCCESS;
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 5e5217b78..4d8e388b6 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -8,12 +8,19 @@
#include <array>
#include <string>
+#include <vector>
+
#include "common/common.h"
#include "core/hle/result.h"
typedef u32 Handle;
typedef s32 Result;
+// TODO: It would be nice to eventually replace these with strong types that prevent accidental
+// conversion between each other.
+typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space.
+typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space.
+
const Handle INVALID_HANDLE = 0;
namespace Kernel {
@@ -24,7 +31,8 @@ class Thread;
const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
ErrorSummary::OutOfResource, ErrorLevel::Temporary);
// TOOD: Verify code
-const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel);
+const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
enum KernelHandle : Handle {
CurrentThread = 0xFFFF8000,
@@ -50,32 +58,51 @@ enum {
DEFAULT_STACK_SIZE = 0x4000,
};
-class HandleTable;
-
class Object : NonCopyable {
- friend class HandleTable;
- u32 handle = INVALID_HANDLE;
public:
virtual ~Object() {}
- Handle GetHandle() const { return handle; }
+
+ /// Returns a unique identifier for the object. For debugging purposes only.
+ unsigned int GetObjectId() const { return object_id; }
+
virtual std::string GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; }
virtual std::string GetName() const { return "[UNKNOWN KERNEL OBJECT]"; }
virtual Kernel::HandleType GetHandleType() const = 0;
/**
- * Wait for kernel object to synchronize.
- * @return True if the current thread should wait as a result of the wait
+ * Check if a thread can wait on the object
+ * @return True if a thread can wait on the object, otherwise false
*/
- virtual ResultVal<bool> WaitSynchronization() {
- LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
- return UnimplementedFunction(ErrorModule::Kernel);
+ bool IsWaitable() const {
+ switch (GetHandleType()) {
+ case HandleType::Session:
+ case HandleType::Event:
+ case HandleType::Mutex:
+ case HandleType::Thread:
+ case HandleType::Semaphore:
+ case HandleType::Timer:
+ return true;
+
+ case HandleType::Unknown:
+ case HandleType::Port:
+ case HandleType::SharedMemory:
+ case HandleType::Redirection:
+ case HandleType::Process:
+ case HandleType::AddressArbiter:
+ return false;
+ }
+
+ return false;
}
private:
friend void intrusive_ptr_add_ref(Object*);
friend void intrusive_ptr_release(Object*);
+ static unsigned int next_object_id;
+
unsigned int ref_count = 0;
+ unsigned int object_id = next_object_id++;
};
// Special functions used by boost::instrusive_ptr to do automatic ref-counting
@@ -92,6 +119,45 @@ inline void intrusive_ptr_release(Object* object) {
template <typename T>
using SharedPtr = boost::intrusive_ptr<T>;
+/// Class that represents a Kernel object that a thread can be waiting on
+class WaitObject : public Object {
+public:
+
+ /**
+ * Check if the current thread should wait until the object is available
+ * @return True if the current thread should wait due to this object being unavailable
+ */
+ virtual bool ShouldWait() = 0;
+
+ /// Acquire/lock the object if it is available
+ virtual void Acquire() = 0;
+
+ /**
+ * Add a thread to wait on this object
+ * @param thread Pointer to thread to add
+ */
+ void AddWaitingThread(SharedPtr<Thread> thread);
+
+ /**
+ * Removes a thread from waiting on this object (e.g. if it was resumed already)
+ * @param thread Pointer to thread to remove
+ */
+ void RemoveWaitingThread(Thread* thread);
+
+ /**
+ * Wake up the next thread waiting on this object
+ * @return Pointer to the thread that was resumed, nullptr if no threads are waiting
+ */
+ SharedPtr<Thread> WakeupNextThread();
+
+ /// Wake up all threads waiting on this object
+ void WakeupAllWaitingThreads();
+
+private:
+ /// Threads waiting for this object to become available
+ std::vector<SharedPtr<Thread>> waiting_threads;
+};
+
/**
* This class allows the creation of Handles, which are references to objects that can be tested
* for validity and looked up. Here they are used to pass references to kernel objects to/from the
@@ -146,14 +212,14 @@ public:
/**
* Looks up a handle.
- * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid.
+ * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid.
*/
SharedPtr<Object> GetGeneric(Handle handle) const;
/**
* Looks up a handle while verifying its type.
- * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
- * type differs from the handle type `T::HANDLE_TYPE`.
+ * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
+ * type differs from the handle type `T::HANDLE_TYPE`.
*/
template <class T>
SharedPtr<T> Get(Handle handle) const {
@@ -164,6 +230,19 @@ public:
return nullptr;
}
+ /**
+ * Looks up a handle while verifying that it is an object that a thread can wait on
+ * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or it is
+ * not a waitable object.
+ */
+ SharedPtr<WaitObject> GetWaitObject(Handle handle) const {
+ SharedPtr<Object> object = GetGeneric(handle);
+ if (object != nullptr && object->IsWaitable()) {
+ return boost::static_pointer_cast<WaitObject>(std::move(object));
+ }
+ return nullptr;
+ }
+
/// Closes all handles held in this table.
void Clear();
@@ -197,7 +276,6 @@ private:
};
extern HandleTable g_handle_table;
-extern SharedPtr<Thread> g_main_thread;
/// The ID code of the currently running game
/// TODO(Subv): This variable should not be here,
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 853a5dd74..9f7166ca4 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -5,6 +5,8 @@
#include <map>
#include <vector>
+#include <boost/range/algorithm_ext/erase.hpp>
+
#include "common/common.h"
#include "core/hle/kernel/kernel.h"
@@ -13,176 +15,72 @@
namespace Kernel {
-class Mutex : public Object {
-public:
- std::string GetTypeName() const override { return "Mutex"; }
- std::string GetName() const override { return name; }
-
- static const HandleType HANDLE_TYPE = HandleType::Mutex;
- HandleType GetHandleType() const override { return HANDLE_TYPE; }
-
- bool initial_locked; ///< Initial lock state when mutex was created
- bool locked; ///< Current locked state
- Handle lock_thread; ///< Handle to thread that currently has mutex
- std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
- std::string name; ///< Name of mutex (optional)
-
- ResultVal<bool> WaitSynchronization() override;
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-typedef std::multimap<Handle, Handle> MutexMap;
-static MutexMap g_mutex_held_locks;
-
-/**
- * Acquires the specified mutex for the specified thread
- * @param mutex Mutex that is to be acquired
- * @param thread Thread that will acquired
- */
-void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) {
- g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
- mutex->lock_thread = thread;
-}
-
-bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) {
- MutexAcquireLock(mutex, thread_handle);
-
- Thread* thread = Kernel::g_handle_table.Get<Thread>(thread_handle).get();
- if (thread == nullptr) {
- LOG_ERROR(Kernel, "Called with invalid handle: %08X", thread_handle);
- return false;
- }
-
- thread->ResumeFromWait();
- return true;
-}
-
/**
* Resumes a thread waiting for the specified mutex
* @param mutex The mutex that some thread is waiting on
*/
-void ResumeWaitingThread(Mutex* mutex) {
+static void ResumeWaitingThread(Mutex* mutex) {
+ // Reset mutex lock thread handle, nothing is waiting
+ mutex->locked = false;
+ mutex->holding_thread = nullptr;
+
// Find the next waiting thread for the mutex...
- if (mutex->waiting_threads.empty()) {
- // Reset mutex lock thread handle, nothing is waiting
- mutex->locked = false;
- mutex->lock_thread = -1;
- }
- else {
- // Resume the next waiting thread and re-lock the mutex
- std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
- ReleaseMutexForThread(mutex, *iter);
- mutex->waiting_threads.erase(iter);
+ auto next_thread = mutex->WakeupNextThread();
+ if (next_thread != nullptr) {
+ mutex->Acquire(next_thread);
}
}
-void MutexEraseLock(Mutex* mutex) {
- Handle handle = mutex->GetHandle();
- auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
- for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
- if (iter->second == handle) {
- g_mutex_held_locks.erase(iter);
- break;
- }
+void ReleaseThreadMutexes(Thread* thread) {
+ for (auto& mtx : thread->held_mutexes) {
+ ResumeWaitingThread(mtx.get());
}
- mutex->lock_thread = -1;
+ thread->held_mutexes.clear();
}
-void ReleaseThreadMutexes(Handle thread) {
- auto locked = g_mutex_held_locks.equal_range(thread);
-
- // Release every mutex that the thread holds, and resume execution on the waiting threads
- for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
- Mutex* mutex = g_handle_table.Get<Mutex>(iter->second).get();
- ResumeWaitingThread(mutex);
- }
+Mutex::Mutex() {}
+Mutex::~Mutex() {}
- // Erase all the locks that this thread holds
- g_mutex_held_locks.erase(thread);
-}
+SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) {
+ SharedPtr<Mutex> mutex(new Mutex);
-bool LockMutex(Mutex* mutex) {
- // Mutex alread locked?
- if (mutex->locked) {
- return false;
- }
- MutexAcquireLock(mutex);
- return true;
-}
+ mutex->initial_locked = initial_locked;
+ mutex->locked = false;
+ mutex->name = std::move(name);
+ mutex->holding_thread = nullptr;
-bool ReleaseMutex(Mutex* mutex) {
- MutexEraseLock(mutex);
- ResumeWaitingThread(mutex);
- return true;
-}
+ // Acquire mutex with current thread if initialized as locked...
+ if (initial_locked)
+ mutex->Acquire();
-/**
- * Releases a mutex
- * @param handle Handle to mutex to release
- */
-ResultCode ReleaseMutex(Handle handle) {
- Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle).get();
- if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
-
- if (!ReleaseMutex(mutex)) {
- // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure
- // what error condition this is supposed to be signaling.
- return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel,
- ErrorSummary::NothingHappened, ErrorLevel::Temporary);
- }
- return RESULT_SUCCESS;
+ return mutex;
}
-/**
- * Creates a mutex
- * @param handle Reference to handle for the newly created mutex
- * @param initial_locked Specifies if the mutex should be locked initially
- * @param name Optional name of mutex
- * @return Pointer to new Mutex object
- */
-Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) {
- Mutex* mutex = new Mutex;
- // TODO(yuriks): Fix error reporting
- handle = Kernel::g_handle_table.Create(mutex).ValueOr(INVALID_HANDLE);
+bool Mutex::ShouldWait() {
+ return locked && holding_thread != GetCurrentThread();
+}
- mutex->locked = mutex->initial_locked = initial_locked;
- mutex->name = name;
+void Mutex::Acquire() {
+ Acquire(GetCurrentThread());
+}
- // Acquire mutex with current thread if initialized as locked...
- if (mutex->locked) {
- MutexAcquireLock(mutex);
+void Mutex::Acquire(SharedPtr<Thread> thread) {
+ _assert_msg_(Kernel, !ShouldWait(), "object unavailable!");
+ if (locked)
+ return;
- // Otherwise, reset lock thread handle
- } else {
- mutex->lock_thread = -1;
- }
- return mutex;
-}
+ locked = true;
-/**
- * Creates a mutex
- * @param initial_locked Specifies if the mutex should be locked initially
- * @param name Optional name of mutex
- * @return Handle to newly created object
- */
-Handle CreateMutex(bool initial_locked, const std::string& name) {
- Handle handle;
- Mutex* mutex = CreateMutex(handle, initial_locked, name);
- return handle;
+ thread->held_mutexes.insert(this);
+ holding_thread = std::move(thread);
}
-ResultVal<bool> Mutex::WaitSynchronization() {
- bool wait = locked;
- if (locked) {
- waiting_threads.push_back(GetCurrentThread()->GetHandle());
- Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this);
- } else {
- // Lock the mutex when the first thread accesses it
- locked = true;
- MutexAcquireLock(this);
- }
+void Mutex::Release() {
+ if (!locked)
+ return;
- return MakeResult<bool>(wait);
+ holding_thread->held_mutexes.erase(this);
+ ResumeWaitingThread(this);
}
+
} // namespace
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index a8ca97014..548403614 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -4,30 +4,57 @@
#pragma once
+#include <string>
+
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
-/**
- * Releases a mutex
- * @param handle Handle to mutex to release
- */
-ResultCode ReleaseMutex(Handle handle);
-
-/**
- * Creates a mutex
- * @param initial_locked Specifies if the mutex should be locked initially
- * @param name Optional name of mutex
- * @return Handle to newly created object
- */
-Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
+class Thread;
+
+class Mutex final : public WaitObject {
+public:
+ /**
+ * Creates a mutex.
+ * @param initial_locked Specifies if the mutex should be locked initially
+ * @param name Optional name of mutex
+ * @return Pointer to new Mutex object
+ */
+ static SharedPtr<Mutex> Create(bool initial_locked, std::string name = "Unknown");
+
+ std::string GetTypeName() const override { return "Mutex"; }
+ std::string GetName() const override { return name; }
+
+ static const HandleType HANDLE_TYPE = HandleType::Mutex;
+ HandleType GetHandleType() const override { return HANDLE_TYPE; }
+
+ bool initial_locked; ///< Initial lock state when mutex was created
+ bool locked; ///< Current locked state
+ std::string name; ///< Name of mutex (optional)
+ SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex
+
+ bool ShouldWait() override;
+ void Acquire() override;
+
+ /**
+ * Acquires the specified mutex for the specified thread
+ * @param mutex Mutex that is to be acquired
+ * @param thread Thread that will acquire the mutex
+ */
+ void Acquire(SharedPtr<Thread> thread);
+ void Release();
+
+private:
+ Mutex();
+ ~Mutex() override;
+};
/**
* Releases all the mutexes held by the specified thread
* @param thread Thread that is holding the mutexes
*/
-void ReleaseThreadMutexes(Handle thread);
+void ReleaseThreadMutexes(Thread* thread);
} // namespace
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
index 88ec9a104..c8cf8b9a2 100644
--- a/src/core/hle/kernel/semaphore.cpp
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <queue>
-
#include "common/common.h"
#include "core/hle/kernel/kernel.h"
@@ -12,86 +10,51 @@
namespace Kernel {
-class Semaphore : public Object {
-public:
- std::string GetTypeName() const override { return "Semaphore"; }
- std::string GetName() const override { return name; }
-
- static const HandleType HANDLE_TYPE = HandleType::Semaphore;
- HandleType GetHandleType() const override { return HANDLE_TYPE; }
-
- s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
- s32 available_count; ///< Number of free slots left in the semaphore
- std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore
- std::string name; ///< Name of semaphore (optional)
-
- /**
- * Tests whether a semaphore still has free slots
- * @return Whether the semaphore is available
- */
- bool IsAvailable() const {
- return available_count > 0;
- }
-
- ResultVal<bool> WaitSynchronization() override {
- bool wait = !IsAvailable();
-
- if (wait) {
- Kernel::WaitCurrentThread(WAITTYPE_SEMA, this);
- waiting_threads.push(GetCurrentThread()->GetHandle());
- } else {
- --available_count;
- }
-
- return MakeResult<bool>(wait);
- }
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
+Semaphore::Semaphore() {}
+Semaphore::~Semaphore() {}
-ResultCode CreateSemaphore(Handle* handle, s32 initial_count,
- s32 max_count, const std::string& name) {
+ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count,
+ std::string name) {
if (initial_count > max_count)
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel,
ErrorSummary::WrongArgument, ErrorLevel::Permanent);
- Semaphore* semaphore = new Semaphore;
- // TOOD(yuriks): Fix error reporting
- *handle = g_handle_table.Create(semaphore).ValueOr(INVALID_HANDLE);
+ SharedPtr<Semaphore> semaphore(new Semaphore);
// When the semaphore is created, some slots are reserved for other threads,
// and the rest is reserved for the caller thread
semaphore->max_count = max_count;
semaphore->available_count = initial_count;
- semaphore->name = name;
+ semaphore->name = std::move(name);
- return RESULT_SUCCESS;
+ return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore));
}
-ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
- Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle).get();
- if (semaphore == nullptr)
- return InvalidHandle(ErrorModule::Kernel);
+bool Semaphore::ShouldWait() {
+ return available_count <= 0;
+}
+
+void Semaphore::Acquire() {
+ _assert_msg_(Kernel, !ShouldWait(), "object unavailable!");
+ --available_count;
+}
- if (semaphore->max_count - semaphore->available_count < release_count)
+ResultVal<s32> Semaphore::Release(s32 release_count) {
+ if (max_count - available_count < release_count)
return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
- *count = semaphore->available_count;
- semaphore->available_count += release_count;
+ s32 previous_count = available_count;
+ available_count += release_count;
// Notify some of the threads that the semaphore has been released
// stop once the semaphore is full again or there are no more waiting threads
- while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) {
- Thread* thread = Kernel::g_handle_table.Get<Thread>(semaphore->waiting_threads.front()).get();
- if (thread != nullptr)
- thread->ResumeFromWait();
- semaphore->waiting_threads.pop();
- --semaphore->available_count;
+ while (!ShouldWait() && WakeupNextThread() != nullptr) {
+ Acquire();
}
- return RESULT_SUCCESS;
+ return MakeResult<s32>(previous_count);
}
} // namespace
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
index 8644ecf0c..d8dc1fd78 100644
--- a/src/core/hle/kernel/semaphore.h
+++ b/src/core/hle/kernel/semaphore.h
@@ -4,29 +4,51 @@
#pragma once
+#include <queue>
+#include <string>
+
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
-/**
- * Creates a semaphore.
- * @param handle Pointer to the handle of the newly created object
- * @param initial_count Number of slots reserved for other threads
- * @param max_count Maximum number of slots the semaphore can have
- * @param name Optional name of semaphore
- * @return ResultCode of the error
- */
-ResultCode CreateSemaphore(Handle* handle, s32 initial_count, s32 max_count, const std::string& name = "Unknown");
-
-/**
- * Releases a certain number of slots from a semaphore.
- * @param count The number of free slots the semaphore had before this call
- * @param handle The handle of the semaphore to release
- * @param release_count The number of slots to release
- * @return ResultCode of the error
- */
-ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count);
+class Semaphore final : public WaitObject {
+public:
+ /**
+ * Creates a semaphore.
+ * @param handle Pointer to the handle of the newly created object
+ * @param initial_count Number of slots reserved for other threads
+ * @param max_count Maximum number of slots the semaphore can have
+ * @param name Optional name of semaphore
+ * @return The created semaphore
+ */
+ static ResultVal<SharedPtr<Semaphore>> Create(s32 initial_count, s32 max_count,
+ std::string name = "Unknown");
+
+ std::string GetTypeName() const override { return "Semaphore"; }
+ std::string GetName() const override { return name; }
+
+ static const HandleType HANDLE_TYPE = HandleType::Semaphore;
+ HandleType GetHandleType() const override { return HANDLE_TYPE; }
+
+ s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
+ s32 available_count; ///< Number of free slots left in the semaphore
+ std::string name; ///< Name of semaphore (optional)
+
+ bool ShouldWait() override;
+ void Acquire() override;
+
+ /**
+ * Releases a certain number of slots from a semaphore.
+ * @param release_count The number of slots to release
+ * @return The number of free slots the semaphore had before this call
+ */
+ ResultVal<s32> Release(s32 release_count);
+
+private:
+ Semaphore();
+ ~Semaphore() override;
+};
} // namespace
diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp
new file mode 100644
index 000000000..0594967f8
--- /dev/null
+++ b/src/core/hle/kernel/session.cpp
@@ -0,0 +1,13 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/session.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+Session::Session() {}
+Session::~Session() {}
+
+}
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
index 91f3ffc2c..7cc9332c9 100644
--- a/src/core/hle/kernel/session.h
+++ b/src/core/hle/kernel/session.h
@@ -5,6 +5,7 @@
#pragma once
#include "core/hle/kernel/kernel.h"
+#include "core/mem_map.h"
namespace Kernel {
@@ -41,8 +42,11 @@ inline static u32* GetCommandBuffer(const int offset=0) {
* CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as
* opposed to HLE simulations.
*/
-class Session : public Object {
+class Session : public WaitObject {
public:
+ Session();
+ ~Session() override;
+
std::string GetTypeName() const override { return "Session"; }
static const HandleType HANDLE_TYPE = HandleType::Session;
@@ -53,6 +57,17 @@ public:
* aren't supported yet.
*/
virtual ResultVal<bool> SyncRequest() = 0;
+
+ // TODO(bunnei): These functions exist to satisfy a hardware test with a Session object
+ // passed into WaitSynchronization. Figure out the meaning of them.
+
+ bool ShouldWait() override {
+ return true;
+ }
+
+ void Acquire() override {
+ _assert_msg_(Kernel, !ShouldWait(), "object unavailable!");
+ }
};
}
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 5368e4728..4211fcf04 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -9,76 +9,40 @@
namespace Kernel {
-class SharedMemory : public Object {
-public:
- std::string GetTypeName() const override { return "SharedMemory"; }
+SharedMemory::SharedMemory() {}
+SharedMemory::~SharedMemory() {}
- static const HandleType HANDLE_TYPE = HandleType::SharedMemory;
- HandleType GetHandleType() const override { return HANDLE_TYPE; }
+SharedPtr<SharedMemory> SharedMemory::Create(std::string name) {
+ SharedPtr<SharedMemory> shared_memory(new SharedMemory);
- u32 base_address; ///< Address of shared memory block in RAM
- MemoryPermission permissions; ///< Permissions of shared memory block (SVC field)
- MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field)
- std::string name; ///< Name of shared memory object (optional)
-};
+ shared_memory->name = std::move(name);
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Creates a shared memory object
- * @param handle Handle of newly created shared memory object
- * @param name Name of shared memory object
- * @return Pointer to newly created shared memory object
- */
-SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
- SharedMemory* shared_memory = new SharedMemory;
- // TOOD(yuriks): Fix error reporting
- handle = Kernel::g_handle_table.Create(shared_memory).ValueOr(INVALID_HANDLE);
- shared_memory->name = name;
return shared_memory;
}
-Handle CreateSharedMemory(const std::string& name) {
- Handle handle;
- CreateSharedMemory(handle, name);
- return handle;
-}
-
-/**
- * Maps a shared memory block to an address in system memory
- * @param handle Shared memory block handle
- * @param address Address in system memory to map shared memory block to
- * @param permissions Memory block map permissions (specified by SVC field)
- * @param other_permissions Memory block map other permissions (specified by SVC field)
- * @return Result of operation, 0 on success, otherwise error code
- */
-ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
- MemoryPermission other_permissions) {
+ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions,
+ MemoryPermission other_permissions) {
if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
- LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
- handle, address);
+ LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X outside of shared mem bounds!",
+ GetObjectId(), address);
+ // TODO: Verify error code with hardware
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
- SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get();
- if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
- shared_memory->base_address = address;
- shared_memory->permissions = permissions;
- shared_memory->other_permissions = other_permissions;
+ this->base_address = address;
+ this->permissions = permissions;
+ this->other_permissions = other_permissions;
return RESULT_SUCCESS;
}
-ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
- SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get();
- if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
-
- if (0 != shared_memory->base_address)
- return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset));
+ResultVal<u8*> SharedMemory::GetPointer(u32 offset) {
+ if (base_address != 0)
+ return MakeResult<u8*>(Memory::GetPointer(base_address + offset));
- LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle);
+ LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId());
// TODO(yuriks): Verify error code.
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidState, ErrorLevel::Permanent);
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index bb65c7ccd..5833b411c 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -23,29 +23,42 @@ enum class MemoryPermission : u32 {
DontCare = (1u << 28)
};
-/**
- * Creates a shared memory object
- * @param name Optional name of shared memory object
- * @return Handle of newly created shared memory object
- */
-Handle CreateSharedMemory(const std::string& name="Unknown");
-
-/**
- * Maps a shared memory block to an address in system memory
- * @param handle Shared memory block handle
- * @param address Address in system memory to map shared memory block to
- * @param permissions Memory block map permissions (specified by SVC field)
- * @param other_permissions Memory block map other permissions (specified by SVC field)
- */
-ResultCode MapSharedMemory(Handle handle, u32 address, MemoryPermission permissions,
- MemoryPermission other_permissions);
-
-/**
- * Gets a pointer to the shared memory block
- * @param handle Shared memory block handle
- * @param offset Offset from the start of the shared memory block to get pointer
- * @return Pointer to the shared memory block from the specified offset
- */
-ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset);
+class SharedMemory final : public Object {
+public:
+ /**
+ * Creates a shared memory object
+ * @param name Optional object name, used only for debugging purposes.
+ */
+ static SharedPtr<SharedMemory> Create(std::string name = "Unknown");
+
+ std::string GetTypeName() const override { return "SharedMemory"; }
+
+ static const HandleType HANDLE_TYPE = HandleType::SharedMemory;
+ HandleType GetHandleType() const override { return HANDLE_TYPE; }
+
+ /**
+ * Maps a shared memory block to an address in system memory
+ * @param address Address in system memory to map shared memory block to
+ * @param permissions Memory block map permissions (specified by SVC field)
+ * @param other_permissions Memory block map other permissions (specified by SVC field)
+ */
+ ResultCode Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions);
+
+ /**
+ * Gets a pointer to the shared memory block
+ * @param offset Offset from the start of the shared memory block to get pointer
+ * @return Pointer to the shared memory block from the specified offset
+ */
+ ResultVal<u8*> GetPointer(u32 offset = 0);
+
+ VAddr base_address; ///< Address of shared memory block in RAM
+ MemoryPermission permissions; ///< Permissions of shared memory block (SVC field)
+ MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field)
+ std::string name; ///< Name of shared memory object (optional)
+
+private:
+ SharedMemory();
+ ~SharedMemory() override;
+};
} // namespace
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index dd20ca30e..3987f9608 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -4,7 +4,6 @@
#include <algorithm>
#include <list>
-#include <map>
#include <vector>
#include "common/common.h"
@@ -22,17 +21,12 @@
namespace Kernel {
-ResultVal<bool> Thread::WaitSynchronization() {
- const bool wait = status != THREADSTATUS_DORMANT;
- if (wait) {
- Thread* thread = GetCurrentThread();
- if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
- waiting_threads.push_back(thread);
- }
- WaitCurrentThread(WAITTYPE_THREADEND, this);
- }
+bool Thread::ShouldWait() {
+ return status != THREADSTATUS_DORMANT;
+}
- return MakeResult<bool>(wait);
+void Thread::Acquire() {
+ _assert_msg_(Kernel, !ShouldWait(), "object unavailable!");
}
// Lists all thread ids that aren't deleted/etc.
@@ -46,6 +40,9 @@ static Thread* current_thread;
static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
static u32 next_thread_id; ///< The next available thread id
+Thread::Thread() {}
+Thread::~Thread() {}
+
Thread* GetCurrentThread() {
return current_thread;
}
@@ -55,7 +52,7 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
memset(&t->context, 0, sizeof(Core::ThreadContext));
t->context.cpu_registers[0] = arg;
- t->context.pc = t->context.reg_15 = t->entry_point;
+ t->context.pc = t->entry_point;
t->context.sp = t->stack_top;
t->context.cpsr = 0x1F; // Usermode
@@ -67,8 +64,8 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
if (t->current_priority < lowest_priority) {
t->current_priority = t->initial_priority;
}
- t->wait_type = WAITTYPE_NONE;
- t->wait_object = nullptr;
+
+ t->wait_objects.clear();
t->wait_address = 0;
}
@@ -88,37 +85,35 @@ static void ChangeReadyState(Thread* t, bool ready) {
}
}
-/// Check if a thread is blocking on a specified wait type
-static bool CheckWaitType(const Thread* thread, WaitType type) {
- return (type == thread->wait_type) && (thread->IsWaiting());
-}
+/// Check if a thread is waiting on a the specified wait object
+static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) {
+ auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object);
+
+ if (itr != thread->wait_objects.end())
+ return thread->IsWaiting();
-/// Check if a thread is blocking on a specified wait type with a specified handle
-static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) {
- return CheckWaitType(thread, type) && wait_object == thread->wait_object;
+ return false;
}
-/// Check if a thread is blocking on a specified wait type with a specified handle and address
-static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) {
- return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address);
+/// Check if the specified thread is waiting on the specified address to be arbitrated
+static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
+ return thread->IsWaiting() && thread->wait_objects.empty() && wait_address == thread->wait_address;
}
/// Stops the current thread
void Thread::Stop(const char* reason) {
// Release all the mutexes that this thread holds
- ReleaseThreadMutexes(GetHandle());
+ ReleaseThreadMutexes(this);
ChangeReadyState(this, false);
status = THREADSTATUS_DORMANT;
- for (auto& waiting_thread : waiting_threads) {
- if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this))
- waiting_thread->ResumeFromWait();
- }
- waiting_threads.clear();
+ WakeupAllWaitingThreads();
// Stopped threads are never waiting.
- wait_type = WAITTYPE_NONE;
- wait_object = nullptr;
+ for (auto& wait_object : wait_objects) {
+ wait_object->RemoveWaitingThread(this);
+ }
+ wait_objects.clear();
wait_address = 0;
}
@@ -129,26 +124,20 @@ static void ChangeThreadState(Thread* t, ThreadStatus new_status) {
}
ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0);
t->status = new_status;
-
- if (new_status == THREADSTATUS_WAIT) {
- if (t->wait_type == WAITTYPE_NONE) {
- LOG_ERROR(Kernel, "Waittype none not allowed");
- }
- }
}
/// Arbitrate the highest priority thread that is waiting
-Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) {
+Thread* ArbitrateHighestPriorityThread(u32 address) {
Thread* highest_priority_thread = nullptr;
s32 priority = THREADPRIO_LOWEST;
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
for (auto& thread : thread_list) {
- if (!CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address))
+ if (!CheckWait_AddressArbiter(thread.get(), address))
continue;
if (thread == nullptr)
- continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
+ continue;
if(thread->current_priority <= priority) {
highest_priority_thread = thread.get();
@@ -165,11 +154,11 @@ Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) {
}
/// Arbitrate all threads currently waiting
-void ArbitrateAllThreads(Object* arbiter, u32 address) {
+void ArbitrateAllThreads(u32 address) {
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
for (auto& thread : thread_list) {
- if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address))
+ if (CheckWait_AddressArbiter(thread.get(), address))
thread->ResumeFromWait();
}
}
@@ -177,9 +166,6 @@ void ArbitrateAllThreads(Object* arbiter, u32 address) {
/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields)
static void CallThread(Thread* t) {
// Stop waiting
- if (t->wait_type != WAITTYPE_NONE) {
- t->wait_type = WAITTYPE_NONE;
- }
ChangeThreadState(t, THREADSTATUS_READY);
}
@@ -200,7 +186,6 @@ static void SwitchContext(Thread* t) {
current_thread = t;
ChangeReadyState(t, false);
t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY;
- t->wait_type = WAITTYPE_NONE;
Core::g_app_core->LoadContext(t->context);
} else {
current_thread = nullptr;
@@ -223,49 +208,119 @@ static Thread* NextThread() {
return next;
}
-void WaitCurrentThread(WaitType wait_type, Object* wait_object) {
+void WaitCurrentThread_Sleep() {
Thread* thread = GetCurrentThread();
- thread->wait_type = wait_type;
- thread->wait_object = wait_object;
ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
}
-void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) {
- WaitCurrentThread(wait_type, wait_object);
- GetCurrentThread()->wait_address = wait_address;
+void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) {
+ Thread* thread = GetCurrentThread();
+ thread->wait_set_output = wait_set_output;
+ thread->wait_all = wait_all;
+
+ // It's possible to call WaitSynchronizationN without any objects passed in...
+ if (wait_object != nullptr)
+ thread->wait_objects.push_back(wait_object);
+
+ ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
+}
+
+void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
+ Thread* thread = GetCurrentThread();
+ thread->wait_address = wait_address;
+ ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
}
/// Event type for the thread wake up event
static int ThreadWakeupEventType = -1;
+// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing
+// us to simply use a pool index or similar.
+static Kernel::HandleTable wakeup_callback_handle_table;
/// Callback that will wake up the thread it was scheduled for
-static void ThreadWakeupCallback(u64 parameter, int cycles_late) {
- Handle handle = static_cast<Handle>(parameter);
- SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(handle);
+static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
+ SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle);
if (thread == nullptr) {
- LOG_ERROR(Kernel, "Thread doesn't exist %u", handle);
+ LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", thread_handle);
return;
}
+ thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
+ ErrorSummary::StatusChanged, ErrorLevel::Info));
+
+ if (thread->wait_set_output)
+ thread->SetWaitSynchronizationOutput(-1);
+
thread->ResumeFromWait();
}
-void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) {
+void Thread::WakeAfterDelay(s64 nanoseconds) {
// Don't schedule a wakeup if the thread wants to wait forever
if (nanoseconds == -1)
return;
- _dbg_assert_(Kernel, thread != nullptr);
u64 microseconds = nanoseconds / 1000;
- CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle());
+ CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle);
+}
+
+void Thread::ReleaseWaitObject(WaitObject* wait_object) {
+ if (wait_objects.empty()) {
+ LOG_CRITICAL(Kernel, "thread is not waiting on any objects!");
+ return;
+ }
+
+ // Remove this thread from the waiting object's thread list
+ wait_object->RemoveWaitingThread(this);
+
+ unsigned index = 0;
+ bool wait_all_failed = false; // Will be set to true if any object is unavailable
+
+ // Iterate through all waiting objects to check availability...
+ for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) {
+ if ((*itr)->ShouldWait())
+ wait_all_failed = true;
+
+ // The output should be the last index of wait_object
+ if (*itr == wait_object)
+ index = itr - wait_objects.begin();
+ }
+
+ // If we are waiting on all objects...
+ if (wait_all) {
+ // Resume the thread only if all are available...
+ if (!wait_all_failed) {
+ SetWaitSynchronizationResult(RESULT_SUCCESS);
+ SetWaitSynchronizationOutput(-1);
+
+ ResumeFromWait();
+ }
+ } else {
+ // Otherwise, resume
+ SetWaitSynchronizationResult(RESULT_SUCCESS);
+
+ if (wait_set_output)
+ SetWaitSynchronizationOutput(index);
+
+ ResumeFromWait();
+ }
}
-/// Resumes a thread from waiting by marking it as "ready"
void Thread::ResumeFromWait() {
+ // Cancel any outstanding wakeup events
+ CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
+
status &= ~THREADSTATUS_WAIT;
- wait_object = nullptr;
- wait_type = WAITTYPE_NONE;
+
+ // Remove this thread from all other WaitObjects
+ for (auto wait_object : wait_objects)
+ wait_object->RemoveWaitingThread(this);
+
+ wait_objects.clear();
+ wait_set_output = false;
+ wait_all = false;
+ wait_address = 0;
+
if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
ChangeReadyState(this, true);
}
@@ -277,11 +332,11 @@ static void DebugThreadQueue() {
if (!thread) {
return;
}
- LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThread()->GetHandle());
+ LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, GetCurrentThread()->GetObjectId());
for (auto& t : thread_list) {
s32 priority = thread_ready_queue.contains(t.get());
if (priority != -1) {
- LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, t->GetHandle());
+ LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId());
}
}
}
@@ -313,14 +368,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
SharedPtr<Thread> thread(new Thread);
- // TODO(yuriks): Thread requires a handle to be inserted into the various scheduling queues for
- // the time being. Create a handle here, it will be copied to the handle field in
- // the object and use by the rest of the code. This should be removed when other
- // code doesn't rely on the handle anymore.
- ResultVal<Handle> handle = Kernel::g_handle_table.Create(thread);
- if (handle.Failed())
- return handle.Code();
-
thread_list.push_back(thread);
thread_ready_queue.prepare(priority);
@@ -331,10 +378,12 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->stack_size = stack_size;
thread->initial_priority = thread->current_priority = priority;
thread->processor_id = processor_id;
- thread->wait_type = WAITTYPE_NONE;
- thread->wait_object = nullptr;
+ thread->wait_set_output = false;
+ thread->wait_all = false;
+ thread->wait_objects.clear();
thread->wait_address = 0;
thread->name = std::move(name);
+ thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom();
ResetThread(thread.get(), arg, 0);
CallThread(thread.get());
@@ -368,16 +417,14 @@ void Thread::SetPriority(s32 priority) {
}
}
-Handle SetupIdleThread() {
+SharedPtr<Thread> SetupIdleThread() {
// We need to pass a few valid values to get around parameter checking in Thread::Create.
- auto thread_res = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0,
- THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE);
- _dbg_assert_(Kernel, thread_res.Succeeded());
- SharedPtr<Thread> thread = std::move(*thread_res);
+ auto thread = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0,
+ THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE).MoveFrom();
thread->idle = true;
CallThread(thread.get());
- return thread->GetHandle();
+ return thread;
}
SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) {
@@ -410,19 +457,26 @@ void Reschedule() {
HLE::g_reschedule = false;
if (next != nullptr) {
- LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
+ LOG_TRACE(Kernel, "context switch %u -> %u", prev->GetObjectId(), next->GetObjectId());
SwitchContext(next);
} else {
- LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle());
+ LOG_TRACE(Kernel, "cannot context switch from %u, no higher priority thread!", prev->GetObjectId());
for (auto& thread : thread_list) {
- LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X",
- thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type,
- (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE));
+ LOG_TRACE(Kernel, "\tid=%u prio=0x%02X, status=0x%08X", thread->GetObjectId(),
+ thread->current_priority, thread->status);
}
}
}
+void Thread::SetWaitSynchronizationResult(ResultCode result) {
+ context.cpu_registers[0] = result.raw;
+}
+
+void Thread::SetWaitSynchronizationOutput(s32 output) {
+ context.cpu_registers[1] = output;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
void ThreadingInit() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 284dec400..633bb7c98 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -7,6 +7,8 @@
#include <string>
#include <vector>
+#include <boost/container/flat_set.hpp>
+
#include "common/common_types.h"
#include "core/core.h"
@@ -38,21 +40,11 @@ enum ThreadStatus {
THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND
};
-enum WaitType {
- WAITTYPE_NONE,
- WAITTYPE_SLEEP,
- WAITTYPE_SEMA,
- WAITTYPE_EVENT,
- WAITTYPE_THREADEND,
- WAITTYPE_MUTEX,
- WAITTYPE_SYNCH,
- WAITTYPE_ARB,
- WAITTYPE_TIMER,
-};
-
namespace Kernel {
-class Thread : public Kernel::Object {
+class Mutex;
+
+class Thread final : public WaitObject {
public:
static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority,
u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size);
@@ -70,7 +62,8 @@ public:
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
inline bool IsIdle() const { return idle; }
- ResultVal<bool> WaitSynchronization() override;
+ bool ShouldWait() override;
+ void Acquire() override;
s32 GetPriority() const { return current_priority; }
void SetPriority(s32 priority);
@@ -78,9 +71,34 @@ public:
u32 GetThreadId() const { return thread_id; }
void Stop(const char* reason);
- /// Resumes a thread from waiting by marking it as "ready".
+
+ /**
+ * Release an acquired wait object
+ * @param wait_object WaitObject to release
+ */
+ void ReleaseWaitObject(WaitObject* wait_object);
+
+ /// Resumes a thread from waiting by marking it as "ready"
void ResumeFromWait();
+ /**
+ * Schedules an event to wake up the specified thread after the specified delay.
+ * @param nanoseconds The time this thread will be allowed to sleep for.
+ */
+ void WakeAfterDelay(s64 nanoseconds);
+
+ /**
+ * Sets the result after the thread awakens (from either WaitSynchronization SVC)
+ * @param result Value to set to the returned result
+ */
+ void SetWaitSynchronizationResult(ResultCode result);
+
+ /**
+ * Sets the output parameter value after the thread awakens (from WaitSynchronizationN SVC only)
+ * @param output Value to set to the output parameter
+ */
+ void SetWaitSynchronizationOutput(s32 output);
+
Core::ThreadContext context;
u32 thread_id;
@@ -95,11 +113,13 @@ public:
s32 processor_id;
- WaitType wait_type;
- Object* wait_object;
- VAddr wait_address;
+ /// Mutexes currently held by this thread, which will be released when it exits.
+ boost::container::flat_set<SharedPtr<Mutex>> held_mutexes;
- std::vector<SharedPtr<Thread>> waiting_threads;
+ std::vector<SharedPtr<WaitObject>> wait_objects; ///< Objects that the thread is waiting on
+ VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address
+ bool wait_all; ///< True if the thread is waiting on all objects before resuming
+ bool wait_set_output; ///< True if the output parameter should be set on thread wakeup
std::string name;
@@ -107,9 +127,15 @@ public:
bool idle = false;
private:
- Thread() = default;
+ Thread();
+ ~Thread() override;
+
+ /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
+ Handle callback_handle;
};
+extern SharedPtr<Thread> g_main_thread;
+
/// Sets up the primary application thread
SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size);
@@ -117,37 +143,30 @@ SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size);
void Reschedule();
/// Arbitrate the highest priority thread that is waiting
-Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address);
+Thread* ArbitrateHighestPriorityThread(u32 address);
/// Arbitrate all threads currently waiting...
-void ArbitrateAllThreads(Object* arbiter, u32 address);
+void ArbitrateAllThreads(u32 address);
/// Gets the current thread
Thread* GetCurrentThread();
-/**
- * Puts the current thread in the wait state for the given type
- * @param wait_type Type of wait
- * @param wait_object Kernel object that we are waiting on, defaults to current thread
- */
-void WaitCurrentThread(WaitType wait_type, Object* wait_object = GetCurrentThread());
+/// Waits the current thread on a sleep
+void WaitCurrentThread_Sleep();
/**
- * Schedules an event to wake up the specified thread after the specified delay.
- * @param handle The thread handle.
- * @param nanoseconds The time this thread will be allowed to sleep for.
+ * Waits the current thread from a WaitSynchronization call
+ * @param wait_object Kernel object that we are waiting on
+ * @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only)
+ * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only)
*/
-void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds);
+void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all);
/**
- * Puts the current thread in the wait state for the given type
- * @param wait_type Type of wait
- * @param wait_object Kernel object that we are waiting on
+ * Waits the current thread from an ArbitrateAddress call
* @param wait_address Arbitration address used to resume from wait
*/
-void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address);
-
-
+void WaitCurrentThread_ArbitrateAddress(VAddr wait_address);
/**
* Sets up the idle thread, this is a thread that is intended to never execute instructions,
@@ -155,7 +174,8 @@ void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_addre
* and will try to yield on every call.
* @returns The handle of the idle thread
*/
-Handle SetupIdleThread();
+SharedPtr<Thread> SetupIdleThread();
+
/// Initialize threading
void ThreadingInit();
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index 3b0452d4d..4352fc99c 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <set>
-
#include "common/common.h"
#include "core/core_timing.h"
@@ -13,77 +11,62 @@
namespace Kernel {
-class Timer : public Object {
-public:
- std::string GetTypeName() const override { return "Timer"; }
- std::string GetName() const override { return name; }
-
- static const HandleType HANDLE_TYPE = HandleType::Timer;
- HandleType GetHandleType() const override { return HANDLE_TYPE; }
-
- ResetType reset_type; ///< The ResetType of this timer
-
- bool signaled; ///< Whether the timer has been signaled or not
- std::set<Handle> waiting_threads; ///< Threads that are waiting for the timer
- std::string name; ///< Name of timer (optional)
-
- u64 initial_delay; ///< The delay until the timer fires for the first time
- u64 interval_delay; ///< The delay until the timer fires after the first time
-
- ResultVal<bool> WaitSynchronization() override {
- bool wait = !signaled;
- if (wait) {
- waiting_threads.insert(GetCurrentThread()->GetHandle());
- Kernel::WaitCurrentThread(WAITTYPE_TIMER, this);
- }
- return MakeResult<bool>(wait);
- }
-};
+/// The event type of the generic timer callback event
+static int timer_callback_event_type = -1;
+// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future, allowing
+// us to simply use a pool index or similar.
+static Kernel::HandleTable timer_callback_handle_table;
-/**
- * Creates a timer.
- * @param handle Reference to handle for the newly created timer
- * @param reset_type ResetType describing how to create timer
- * @param name Optional name of timer
- * @return Newly created Timer object
- */
-Timer* CreateTimer(Handle& handle, const ResetType reset_type, const std::string& name) {
- Timer* timer = new Timer;
+Timer::Timer() {}
+Timer::~Timer() {}
- handle = Kernel::g_handle_table.Create(timer).ValueOr(INVALID_HANDLE);
+SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) {
+ SharedPtr<Timer> timer(new Timer);
timer->reset_type = reset_type;
timer->signaled = false;
- timer->name = name;
+ timer->name = std::move(name);
timer->initial_delay = 0;
timer->interval_delay = 0;
+ timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom();
+
return timer;
}
-ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name) {
- CreateTimer(*handle, reset_type, name);
- return RESULT_SUCCESS;
+bool Timer::ShouldWait() {
+ return !signaled;
+}
+
+void Timer::Acquire() {
+ _assert_msg_(Kernel, !ShouldWait(), "object unavailable!");
}
-ResultCode ClearTimer(Handle handle) {
- SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
-
- if (timer == nullptr)
- return InvalidHandle(ErrorModule::Kernel);
+void Timer::Set(s64 initial, s64 interval) {
+ // Ensure we get rid of any previous scheduled event
+ Cancel();
- timer->signaled = false;
- return RESULT_SUCCESS;
+ initial_delay = initial;
+ interval_delay = interval;
+
+ u64 initial_microseconds = initial / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(initial_microseconds),
+ timer_callback_event_type, callback_handle);
}
-/// The event type of the generic timer callback event
-static int TimerCallbackEventType = -1;
+void Timer::Cancel() {
+ CoreTiming::UnscheduleEvent(timer_callback_event_type, callback_handle);
+}
+
+void Timer::Clear() {
+ signaled = false;
+}
/// The timer callback event, called when a timer is fired
static void TimerCallback(u64 timer_handle, int cycles_late) {
- SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(timer_handle);
+ SharedPtr<Timer> timer = timer_callback_handle_table.Get<Timer>(timer_handle);
if (timer == nullptr) {
- LOG_CRITICAL(Kernel, "Callback fired for invalid timer %u", timer_handle);
+ LOG_CRITICAL(Kernel, "Callback fired for invalid timer %08X", timer_handle);
return;
}
@@ -92,12 +75,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) {
timer->signaled = true;
// Resume all waiting threads
- for (Handle thread_handle : timer->waiting_threads) {
- if (SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(thread_handle))
- thread->ResumeFromWait();
- }
-
- timer->waiting_threads.clear();
+ timer->WakeupAllWaitingThreads();
if (timer->reset_type == RESETTYPE_ONESHOT)
timer->signaled = false;
@@ -106,36 +84,12 @@ static void TimerCallback(u64 timer_handle, int cycles_late) {
// Reschedule the timer with the interval delay
u64 interval_microseconds = timer->interval_delay / 1000;
CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late,
- TimerCallbackEventType, timer_handle);
+ timer_callback_event_type, timer_handle);
}
}
-ResultCode SetTimer(Handle handle, s64 initial, s64 interval) {
- SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
-
- if (timer == nullptr)
- return InvalidHandle(ErrorModule::Kernel);
-
- timer->initial_delay = initial;
- timer->interval_delay = interval;
-
- u64 initial_microseconds = initial / 1000;
- CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), TimerCallbackEventType, handle);
- return RESULT_SUCCESS;
-}
-
-ResultCode CancelTimer(Handle handle) {
- SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
-
- if (timer == nullptr)
- return InvalidHandle(ErrorModule::Kernel);
-
- CoreTiming::UnscheduleEvent(TimerCallbackEventType, handle);
- return RESULT_SUCCESS;
-}
-
void TimersInit() {
- TimerCallbackEventType = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
+ timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
}
void TimersShutdown() {
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h
index f8aa66b60..540e4e187 100644
--- a/src/core/hle/kernel/timer.h
+++ b/src/core/hle/kernel/timer.h
@@ -11,37 +11,54 @@
namespace Kernel {
-/**
- * Cancels a timer
- * @param handle Handle of the timer to cancel
- */
-ResultCode CancelTimer(Handle handle);
-
-/**
- * Starts a timer with the specified initial delay and interval
- * @param handle Handle of the timer to start
- * @param initial Delay until the timer is first fired
- * @param interval Delay until the timer is fired after the first time
- */
-ResultCode SetTimer(Handle handle, s64 initial, s64 interval);
-
-/**
- * Clears a timer
- * @param handle Handle of the timer to clear
- */
-ResultCode ClearTimer(Handle handle);
-
-/**
- * Creates a timer
- * @param Handle to newly created Timer object
- * @param reset_type ResetType describing how to create the timer
- * @param name Optional name of timer
- * @return ResultCode of the error
- */
-ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name="Unknown");
+class Timer final : public WaitObject {
+public:
+ /**
+ * Creates a timer
+ * @param reset_type ResetType describing how to create the timer
+ * @param name Optional name of timer
+ * @return The created Timer
+ */
+ static SharedPtr<Timer> Create(ResetType reset_type, std::string name = "Unknown");
+
+ std::string GetTypeName() const override { return "Timer"; }
+ std::string GetName() const override { return name; }
+
+ static const HandleType HANDLE_TYPE = HandleType::Timer;
+ HandleType GetHandleType() const override { return HANDLE_TYPE; }
+
+ ResetType reset_type; ///< The ResetType of this timer
+
+ bool signaled; ///< Whether the timer has been signaled or not
+ std::string name; ///< Name of timer (optional)
+
+ u64 initial_delay; ///< The delay until the timer fires for the first time
+ u64 interval_delay; ///< The delay until the timer fires after the first time
+
+ bool ShouldWait() override;
+ void Acquire() override;
+
+ /**
+ * Starts the timer, with the specified initial delay and interval.
+ * @param initial Delay until the timer is first fired
+ * @param interval Delay until the timer is fired after the first time
+ */
+ void Set(s64 initial, s64 interval);
+
+ void Cancel();
+ void Clear();
+
+private:
+ Timer();
+ ~Timer() override;
+
+ /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
+ Handle callback_handle;
+};
/// Initializes the required variables for timers
void TimersInit();
/// Tears down the timer variables
void TimersShutdown();
+
} // namespace
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 82dcf5bba..948b9e38e 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -9,8 +9,9 @@
#include <type_traits>
#include <utility>
-#include "common/common_types.h"
#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
// All the constants in this file come from http://3dbrew.org/wiki/Error_codes
@@ -226,11 +227,6 @@ inline ResultCode UnimplementedFunction(ErrorModule module) {
return ResultCode(ErrorDescription::NotImplemented, module,
ErrorSummary::NotSupported, ErrorLevel::Permanent);
}
-/// Returned when a function is passed an invalid handle.
-inline ResultCode InvalidHandle(ErrorModule module) {
- return ResultCode(ErrorDescription::InvalidHandle, module,
- ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
-}
/**
* This is an optional value type. It holds a `ResultCode` and, if that code is a success code,
@@ -364,6 +360,17 @@ public:
return !empty() ? *GetPointer() : std::move(value);
}
+ /// Asserts that the result succeeded and returns a reference to it.
+ T& Unwrap() {
+ // TODO(yuriks): Should be a release assert
+ _assert_msg_(Common, Succeeded(), "Tried to Unwrap empty ResultVal");
+ return **this;
+ }
+
+ T&& MoveFrom() {
+ return std::move(Unwrap());
+ }
+
private:
typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type StorageType;
@@ -400,3 +407,15 @@ template <typename T, typename... Args>
ResultVal<T> MakeResult(Args&&... args) {
return ResultVal<T>::WithCode(RESULT_SUCCESS, std::forward<Args>(args)...);
}
+
+/**
+ * Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps
+ * the contained value and assigns it to `target`, which can be either an l-value expression or a
+ * variable declaration. If it fails the return code is returned from the current function. Thus it
+ * can be used to cascade errors out, achieving something akin to exception handling.
+ */
+#define CASCADE_RESULT(target, source) \
+ auto CONCAT2(check_result_L, __LINE__) = source; \
+ if (CONCAT2(check_result_L, __LINE__).Failed()) \
+ return CONCAT2(check_result_L, __LINE__).Code(); \
+ target = std::move(*CONCAT2(check_result_L, __LINE__))
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp
index 20a3fa2e5..53d920de1 100644
--- a/src/core/hle/service/ac_u.cpp
+++ b/src/core/hle/service/ac_u.cpp
@@ -53,7 +53,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act_u.cpp
index 10870f14b..4ea7a9fb2 100644
--- a/src/core/hle/service/act_u.cpp
+++ b/src/core/hle/service/act_u.cpp
@@ -18,7 +18,7 @@ namespace ACT_U {
// Interface class
Interface::Interface() {
- //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ //Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/am_app.cpp b/src/core/hle/service/am_app.cpp
index 0b396b6d3..df10db87f 100644
--- a/src/core/hle/service/am_app.cpp
+++ b/src/core/hle/service/am_app.cpp
@@ -18,7 +18,7 @@ namespace AM_APP {
// Interface class
Interface::Interface() {
- //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ //Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/am_net.cpp b/src/core/hle/service/am_net.cpp
index 112844e5b..c74012d9d 100644
--- a/src/core/hle/service/am_net.cpp
+++ b/src/core/hle/service/am_net.cpp
@@ -38,7 +38,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/am_sys.cpp b/src/core/hle/service/am_sys.cpp
new file mode 100644
index 000000000..c5df8abda
--- /dev/null
+++ b/src/core/hle/service/am_sys.cpp
@@ -0,0 +1,24 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/am_sys.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_SYS
+
+namespace AM_SYS {
+
+// Empty arrays are illegal -- commented out until an entry is added.
+//const Interface::FunctionInfo FunctionTable[] = { };
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/am_sys.h b/src/core/hle/service/am_sys.h
new file mode 100644
index 000000000..bb6178a43
--- /dev/null
+++ b/src/core/hle/service/am_sys.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_SYS
+
+namespace AM_SYS {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "am:sys";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/apt_a.cpp b/src/core/hle/service/apt_a.cpp
index 37be4b027..e1dd2a5fb 100644
--- a/src/core/hle/service/apt_a.cpp
+++ b/src/core/hle/service/apt_a.cpp
@@ -11,6 +11,7 @@ namespace APT_U {
extern void GetLockHandle(Service::Interface* self);
extern void ReceiveParameter(Service::Interface* self);
extern void GlanceParameter(Service::Interface* self);
+ extern void GetSharedFont(Service::Interface* self);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -29,6 +30,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000E0080, APT_U::GlanceParameter, "GlanceParameter?"},
{0x003B0040, nullptr, "CancelLibraryApplet?"},
{0x00430040, nullptr, "NotifyToWait?"},
+ {0x00440000, APT_U::GetSharedFont, "GetSharedFont?"},
{0x004B00C2, nullptr, "AppletUtility?"},
{0x00550040, nullptr, "WriteInputToNsState?"},
};
@@ -37,7 +39,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/apt_s.cpp b/src/core/hle/service/apt_s.cpp
new file mode 100644
index 000000000..686335428
--- /dev/null
+++ b/src/core/hle/service/apt_s.cpp
@@ -0,0 +1,123 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+
+#include "common/common.h"
+#include "common/file_util.h"
+
+#include "core/hle/hle.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/service/apt_s.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace APT_S
+
+namespace APT_U {
+ extern void GetLockHandle(Service::Interface* self);
+ extern void Initialize(Service::Interface* self);
+ extern void Enable(Service::Interface* self);
+ extern void InquireNotification(Service::Interface* self);
+ extern void NotifyToWait(Service::Interface* self);
+ extern void GetSharedFont(Service::Interface* self);
+ extern void AppletUtility(Service::Interface* self);
+ extern void GlanceParameter(Service::Interface* self);
+ extern void ReceiveParameter(Service::Interface* self);
+}
+
+namespace APT_S {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010040, APT_U::GetLockHandle, "GetLockHandle"},
+ {0x00020080, APT_U::Initialize, "Initialize"},
+ {0x00030040, APT_U::Enable, "Enable"},
+ {0x00040040, nullptr, "Finalize"},
+ {0x00050040, nullptr, "GetAppletManInfo"},
+ {0x00060040, nullptr, "GetAppletInfo"},
+ {0x00070000, nullptr, "GetLastSignaledAppletId"},
+ {0x00080000, nullptr, "CountRegisteredApplet"},
+ {0x00090040, nullptr, "IsRegistered"},
+ {0x000A0040, nullptr, "GetAttribute"},
+ {0x000B0040, APT_U::InquireNotification, "InquireNotification"},
+ {0x000C0104, nullptr, "SendParameter"},
+ {0x000D0080, APT_U::ReceiveParameter, "ReceiveParameter"},
+ {0x000E0080, APT_U::GlanceParameter, "GlanceParameter"},
+ {0x000F0100, nullptr, "CancelParameter"},
+ {0x001000C2, nullptr, "DebugFunc"},
+ {0x001100C0, nullptr, "MapProgramIdForDebug"},
+ {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
+ {0x00130000, nullptr, "GetPreparationState"},
+ {0x00140040, nullptr, "SetPreparationState"},
+ {0x00150140, nullptr, "PrepareToStartApplication"},
+ {0x00160040, nullptr, "PreloadLibraryApplet"},
+ {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
+ {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
+ {0x00190040, nullptr, "PrepareToStartSystemApplet"},
+ {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
+ {0x001B00C4, nullptr, "StartApplication"},
+ {0x001C0000, nullptr, "WakeupApplication"},
+ {0x001D0000, nullptr, "CancelApplication"},
+ {0x001E0084, nullptr, "StartLibraryApplet"},
+ {0x001F0084, nullptr, "StartSystemApplet"},
+ {0x00200044, nullptr, "StartNewestHomeMenu"},
+ {0x00210000, nullptr, "OrderToCloseApplication"},
+ {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00230040, nullptr, "PrepareToJumpToApplication"},
+ {0x00240044, nullptr, "JumpToApplication"},
+ {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
+ {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
+ {0x00270044, nullptr, "CloseApplication"},
+ {0x00280044, nullptr, "CloseLibraryApplet"},
+ {0x00290044, nullptr, "CloseSystemApplet"},
+ {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
+ {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, nullptr, "JumpToHomeMenu"},
+ {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
+ {0x00300044, nullptr, "LeaveResidentApplet"},
+ {0x00310100, nullptr, "PrepareToDoApplicationJump"},
+ {0x00320084, nullptr, "DoApplicationJump"},
+ {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
+ {0x00340084, nullptr, "SendDeliverArg"},
+ {0x00350080, nullptr, "ReceiveDeliverArg"},
+ {0x00360040, nullptr, "LoadSysMenuArg"},
+ {0x00370042, nullptr, "StoreSysMenuArg"},
+ {0x00380040, nullptr, "PreloadResidentApplet"},
+ {0x00390040, nullptr, "PrepareToStartResidentApplet"},
+ {0x003A0044, nullptr, "StartResidentApplet"},
+ {0x003B0040, nullptr, "CancelLibraryApplet"},
+ {0x003C0042, nullptr, "SendDspSleep"},
+ {0x003D0042, nullptr, "SendDspWakeUp"},
+ {0x003E0080, nullptr, "ReplySleepQuery"},
+ {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
+ {0x00400042, nullptr, "SendCaptureBufferInfo"},
+ {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
+ {0x00420080, nullptr, "SleepSystem"},
+ {0x00430040, APT_U::NotifyToWait, "NotifyToWait"},
+ {0x00440000, APT_U::GetSharedFont, "GetSharedFont"},
+ {0x00450040, nullptr, "GetWirelessRebootInfo"},
+ {0x00460104, nullptr, "Wrap"},
+ {0x00470104, nullptr, "Unwrap"},
+ {0x00480100, nullptr, "GetProgramInfo"},
+ {0x00490180, nullptr, "Reboot"},
+ {0x004A0040, nullptr, "GetCaptureInfo"},
+ {0x004B00C2, APT_U::AppletUtility, "AppletUtility"},
+ {0x004C0000, nullptr, "SetFatalErrDispMode"},
+ {0x004D0080, nullptr, "GetAppletProgramInfo"},
+ {0x004E0000, nullptr, "HardwareResetAsync"},
+ {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"},
+ {0x00500040, nullptr, "GetApplicationCpuTimeLimit"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/apt_s.h b/src/core/hle/service/apt_s.h
new file mode 100644
index 000000000..f097c9747
--- /dev/null
+++ b/src/core/hle/service/apt_s.h
@@ -0,0 +1,30 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace APT_S
+
+namespace APT_S {
+
+// Application and title launching service. These services handle signaling for home/power button as
+// well. Only one session for either APT service can be open at a time, normally processes close the
+// service handle immediately once finished using the service. The commands for APT:U and APT:S are
+// exactly the same, however certain commands are only accessible with APT:S(NS module will call
+// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
+
+/// Interface to "APT:S" service
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "APT:S";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp
index d0ff4e585..ccfd04591 100644
--- a/src/core/hle/service/apt_u.cpp
+++ b/src/core/hle/service/apt_u.cpp
@@ -10,7 +10,8 @@
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/shared_memory.h"
-#include "apt_u.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/service/apt_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace APT_U
@@ -25,10 +26,12 @@ namespace APT_U {
// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
static const VAddr SHARED_FONT_VADDR = 0x18000000;
-// Handle to shared memory region designated to for shared system font
-static Handle shared_font_mem = 0;
+/// Handle to shared memory region designated to for shared system font
+static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
-static Handle lock_handle = 0;
+static Kernel::SharedPtr<Kernel::Mutex> lock;
+static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
+static Kernel::SharedPtr<Kernel::Event> pause_event = 0; ///< APT pause event
static std::vector<u8> shared_font;
/// Signals used by APT functions
@@ -39,31 +42,71 @@ enum class SignalType : u32 {
ExitingApp = 0xC,
};
+/// App Id's used by APT functions
+enum class AppID : u32 {
+ HomeMenu = 0x101,
+ AlternateMenu = 0x103,
+ Camera = 0x110,
+ FriendsList = 0x112,
+ GameNotes = 0x113,
+ InternetBrowser = 0x114,
+ InstructionManual = 0x115,
+ Notifications = 0x116,
+ Miiverse = 0x117,
+ SoftwareKeyboard1 = 0x201,
+ Ed = 0x202,
+ PnoteApp = 0x204,
+ SnoteApp = 0x205,
+ Error = 0x206,
+ Mint = 0x207,
+ Extrapad = 0x208,
+ Memolib = 0x209,
+ Application = 0x300,
+ SoftwareKeyboard2 = 0x401,
+};
+
void Initialize(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
- cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
+ // TODO(bunnei): Check if these are created in Initialize or on APT process startup.
+ notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification");
+ pause_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Pause");
+
+ cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom();
+ cmd_buff[4] = Kernel::g_handle_table.Create(pause_event).MoveFrom();
- Kernel::SetEventLocked(cmd_buff[3], true);
- Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event
+ // TODO(bunnei): Check if these events are cleared/signaled every time Initialize is called.
+ notification_event->Clear();
+ pause_event->Signal(); // Fire start event
- _assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock");
- Kernel::ReleaseMutex(lock_handle);
+ _assert_msg_(KERNEL, (nullptr != lock), "Cannot initialize without lock");
+ lock->Release();
- cmd_buff[1] = 0; // No error
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+}
+
+/**
+ * APT_U::NotifyToWait service function
+ * Inputs:
+ * 1 : AppID
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void NotifyToWait(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 app_id = cmd_buff[1];
+ // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further.
+ pause_event->Signal();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id);
}
void GetLockHandle(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
- if (0 == lock_handle) {
- // TODO(bunnei): Verify if this is created here or at application boot?
- lock_handle = Kernel::CreateMutex(false, "APT_U:Lock");
- Kernel::ReleaseMutex(lock_handle);
- }
- cmd_buff[1] = 0; // No error
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
// Not sure what these parameters are used for, but retail apps check that they are 0 after
// GetLockHandle has been called.
@@ -71,26 +114,102 @@ void GetLockHandle(Service::Interface* self) {
cmd_buff[3] = 0;
cmd_buff[4] = 0;
- cmd_buff[5] = lock_handle;
+ cmd_buff[5] = Kernel::g_handle_table.Create(lock).MoveFrom();
LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]);
}
void Enable(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
- cmd_buff[1] = 0; // No error
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
}
+/**
+ * APT_U::GetAppletManInfo service function.
+ * Inputs:
+ * 1 : Unknown
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Unknown u32 value
+ * 3 : Unknown u8 value
+ * 4 : Home Menu AppId
+ * 5 : AppID of currently active app
+ */
+void GetAppletManInfo(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 unk = cmd_buff[1];
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = 0;
+ cmd_buff[3] = 0;
+ cmd_buff[4] = static_cast<u32>(AppID::HomeMenu); // Home menu AppID
+ cmd_buff[5] = static_cast<u32>(AppID::Application); // TODO(purpasmart96): Do this correctly
+
+ LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
+}
+
+/**
+ * APT_U::IsRegistered service function. This returns whether the specified AppID is registered with NS yet.
+ * An AppID is "registered" once the process associated with the AppID uses APT:Enable. Home Menu uses this
+ * command to determine when the launched process is running and to determine when to stop using GSP etc,
+ * while displaying the "Nintendo 3DS" loading screen.
+ * Inputs:
+ * 1 : AppID
+ * Outputs:
+ * 0 : Return header
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output, 0 = not registered, 1 = registered.
+ */
+void IsRegistered(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 app_id = cmd_buff[1];
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = 1; // Set to registered
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
+}
+
void InquireNotification(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[2];
- cmd_buff[1] = 0; // No error
+ u32 app_id = cmd_buff[1];
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
}
/**
+ * APT_U::SendParameter service function. This sets the parameter data state.
+ * Inputs:
+ * 1 : Source AppID
+ * 2 : Destination AppID
+ * 3 : Signal type
+ * 4 : Parameter buffer size, max size is 0x1000 (this can be zero)
+ * 5 : Value
+ * 6 : Handle to the destination process, likely used for shared memory (this can be zero)
+ * 7 : (Size<<14) | 2
+ * 8 : Input parameter buffer ptr
+ * Outputs:
+ * 0 : Return Header
+ * 1 : Result of function, 0 on success, otherwise error code
+*/
+void SendParameter(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 src_app_id = cmd_buff[1];
+ u32 dst_app_id = cmd_buff[2];
+ u32 signal_type = cmd_buff[3];
+ u32 buffer_size = cmd_buff[4];
+ u32 value = cmd_buff[5];
+ u32 handle = cmd_buff[6];
+ u32 size = cmd_buff[7];
+ u32 in_param_buffer_ptr = cmd_buff[8];
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+
+ LOG_WARNING(Service_APT, "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X,"
+ "buffer_size=0x%08X, value=0x%08X, handle=0x%08X, size=0x%08X, in_param_buffer_ptr=0x%08X",
+ src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, in_param_buffer_ptr);
+}
+
+/**
* APT_U::ReceiveParameter service function. This returns the current parameter data from NS state,
* from the source process which set the parameters. Once finished, NS will clear a flag in the NS
* state so that this command will return an error if this command is used again if parameters were
@@ -101,8 +220,8 @@ void InquireNotification(Service::Interface* self) {
* 2 : Parameter buffer size, max size is 0x1000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
- * 2 : Unknown, for now assume AppID of the process which sent these parameters
- * 3 : Unknown, for now assume Signal type
+ * 2 : AppID of the process which sent these parameters
+ * 3 : Signal type
* 4 : Actual parameter buffer size, this is <= to the the input size
* 5 : Value
* 6 : Handle from the source process which set the parameters, likely used for shared memory
@@ -113,7 +232,7 @@ void ReceiveParameter(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[1];
u32 buffer_size = cmd_buff[2];
- cmd_buff[1] = 0; // No error
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = 0;
cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
cmd_buff[4] = 0x10; // Parameter buffer size (16)
@@ -145,7 +264,7 @@ void GlanceParameter(Service::Interface* self) {
u32 app_id = cmd_buff[1];
u32 buffer_size = cmd_buff[2];
- cmd_buff[1] = 0; // No error
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = 0;
cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
cmd_buff[4] = 0x10; // Parameter buffer size (16)
@@ -157,6 +276,36 @@ void GlanceParameter(Service::Interface* self) {
}
/**
+ * APT_U::CancelParameter service function. When the parameter data is available, and when the above
+ * specified fields match the ones in NS state(for the ones where the checks are enabled), this
+ * clears the flag which indicates that parameter data is available
+ * (same flag cleared by APT:ReceiveParameter).
+ * Inputs:
+ * 1 : Flag, when non-zero NS will compare the word after this one with a field in the NS state.
+ * 2 : Unknown, this is the same as the first unknown field returned by APT:ReceiveParameter.
+ * 3 : Flag, when non-zero NS will compare the word after this one with a field in the NS state.
+ * 4 : AppID
+ * Outputs:
+ * 0 : Return header
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Status flag, 0 = failure due to no parameter data being available, or the above enabled
+ * fields don't match the fields in NS state. 1 = success.
+ */
+void CancelParameter(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 flag1 = cmd_buff[1];
+ u32 unk = cmd_buff[2];
+ u32 flag2 = cmd_buff[3];
+ u32 app_id = cmd_buff[4];
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = 1; // Set to Success
+
+ LOG_WARNING(Service_APT, "(STUBBED) called flag1=0x%08X, unk=0x%08X, flag2=0x%08X, app_id=0x%08X",
+ flag1, unk, flag2, app_id);
+}
+
+/**
* APT_U::AppletUtility service function
* Inputs:
* 1 : Unknown, but clearly used for something
@@ -177,7 +326,7 @@ void AppletUtility(Service::Interface* self) {
u32 buffer1_addr = cmd_buff[5];
u32 buffer2_addr = cmd_buff[65];
- cmd_buff[1] = 0; // No error
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, "
"buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size,
@@ -201,96 +350,144 @@ void GetSharedFont(Service::Interface* self) {
memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size());
cmd_buff[0] = 0x00440082;
- cmd_buff[1] = 0; // No error
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = SHARED_FONT_VADDR;
- cmd_buff[4] = shared_font_mem;
+ cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
} else {
cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
}
}
+/**
+ * APT_U::SetAppCpuTimeLimit service function
+ * Inputs:
+ * 1 : Value, must be one
+ * 2 : Percentage of CPU time from 5 to 80
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void SetAppCpuTimeLimit(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 value = cmd_buff[1];
+ u32 percent = cmd_buff[2];
+
+ if (value != 1) {
+ LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
+ }
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+
+ LOG_WARNING(Service_APT, "(STUBBED) called percent=0x%08X, value=0x%08x", percent, value);
+}
+
+/**
+ * APT_U::GetAppCpuTimeLimit service function
+ * Inputs:
+ * 1 : Value, must be one
+ * Outputs:
+ * 0 : Return header
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : System core CPU time percentage
+ */
+void GetAppCpuTimeLimit(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 value = cmd_buff[1];
+
+ if (value != 1) {
+ LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
+ }
+
+ // TODO(purpasmart96): This is incorrect, I'm pretty sure the percentage should
+ // be set by the application.
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = 0x80; // Set to 80%
+
+ LOG_WARNING(Service_APT, "(STUBBED) called value=0x%08x", value);
+}
+
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, GetLockHandle, "GetLockHandle"},
- {0x00020080, Initialize, "Initialize"},
- {0x00030040, Enable, "Enable"},
- {0x00040040, nullptr, "Finalize"},
- {0x00050040, nullptr, "GetAppletManInfo"},
- {0x00060040, nullptr, "GetAppletInfo"},
- {0x00070000, nullptr, "GetLastSignaledAppletId"},
- {0x00080000, nullptr, "CountRegisteredApplet"},
- {0x00090040, nullptr, "IsRegistered"},
- {0x000A0040, nullptr, "GetAttribute"},
- {0x000B0040, InquireNotification, "InquireNotification"},
- {0x000C0104, nullptr, "SendParameter"},
- {0x000D0080, ReceiveParameter, "ReceiveParameter"},
- {0x000E0080, GlanceParameter, "GlanceParameter"},
- {0x000F0100, nullptr, "CancelParameter"},
- {0x001000C2, nullptr, "DebugFunc"},
- {0x001100C0, nullptr, "MapProgramIdForDebug"},
- {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
- {0x00130000, nullptr, "GetPreparationState"},
- {0x00140040, nullptr, "SetPreparationState"},
- {0x00150140, nullptr, "PrepareToStartApplication"},
- {0x00160040, nullptr, "PreloadLibraryApplet"},
- {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
- {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
- {0x00190040, nullptr, "PrepareToStartSystemApplet"},
- {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
- {0x001B00C4, nullptr, "StartApplication"},
- {0x001C0000, nullptr, "WakeupApplication"},
- {0x001D0000, nullptr, "CancelApplication"},
- {0x001E0084, nullptr, "StartLibraryApplet"},
- {0x001F0084, nullptr, "StartSystemApplet"},
- {0x00200044, nullptr, "StartNewestHomeMenu"},
- {0x00210000, nullptr, "OrderToCloseApplication"},
- {0x00220040, nullptr, "PrepareToCloseApplication"},
- {0x00230040, nullptr, "PrepareToJumpToApplication"},
- {0x00240044, nullptr, "JumpToApplication"},
- {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
- {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
- {0x00270044, nullptr, "CloseApplication"},
- {0x00280044, nullptr, "CloseLibraryApplet"},
- {0x00290044, nullptr, "CloseSystemApplet"},
- {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
- {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
- {0x002C0044, nullptr, "JumpToHomeMenu"},
- {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
- {0x002E0044, nullptr, "LeaveHomeMenu"},
- {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
- {0x00300044, nullptr, "LeaveResidentApplet"},
- {0x00310100, nullptr, "PrepareToDoApplicationJump"},
- {0x00320084, nullptr, "DoApplicationJump"},
- {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
- {0x00340084, nullptr, "SendDeliverArg"},
- {0x00350080, nullptr, "ReceiveDeliverArg"},
- {0x00360040, nullptr, "LoadSysMenuArg"},
- {0x00370042, nullptr, "StoreSysMenuArg"},
- {0x00380040, nullptr, "PreloadResidentApplet"},
- {0x00390040, nullptr, "PrepareToStartResidentApplet"},
- {0x003A0044, nullptr, "StartResidentApplet"},
- {0x003B0040, nullptr, "CancelLibraryApplet"},
- {0x003C0042, nullptr, "SendDspSleep"},
- {0x003D0042, nullptr, "SendDspWakeUp"},
- {0x003E0080, nullptr, "ReplySleepQuery"},
- {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
- {0x00400042, nullptr, "SendCaptureBufferInfo"},
- {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
- {0x00420080, nullptr, "SleepSystem"},
- {0x00430040, nullptr, "NotifyToWait"},
- {0x00440000, GetSharedFont, "GetSharedFont"},
- {0x00450040, nullptr, "GetWirelessRebootInfo"},
- {0x00460104, nullptr, "Wrap"},
- {0x00470104, nullptr, "Unwrap"},
- {0x00480100, nullptr, "GetProgramInfo"},
- {0x00490180, nullptr, "Reboot"},
- {0x004A0040, nullptr, "GetCaptureInfo"},
- {0x004B00C2, AppletUtility, "AppletUtility"},
- {0x004C0000, nullptr, "SetFatalErrDispMode"},
- {0x004D0080, nullptr, "GetAppletProgramInfo"},
- {0x004E0000, nullptr, "HardwareResetAsync"},
- {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"},
- {0x00500040, nullptr, "GetApplicationCpuTimeLimit"},
+ {0x00010040, GetLockHandle, "GetLockHandle"},
+ {0x00020080, Initialize, "Initialize"},
+ {0x00030040, Enable, "Enable"},
+ {0x00040040, nullptr, "Finalize"},
+ {0x00050040, GetAppletManInfo, "GetAppletManInfo"},
+ {0x00060040, nullptr, "GetAppletInfo"},
+ {0x00070000, nullptr, "GetLastSignaledAppletId"},
+ {0x00080000, nullptr, "CountRegisteredApplet"},
+ {0x00090040, IsRegistered, "IsRegistered"},
+ {0x000A0040, nullptr, "GetAttribute"},
+ {0x000B0040, InquireNotification, "InquireNotification"},
+ {0x000C0104, SendParameter, "SendParameter"},
+ {0x000D0080, ReceiveParameter, "ReceiveParameter"},
+ {0x000E0080, GlanceParameter, "GlanceParameter"},
+ {0x000F0100, CancelParameter, "CancelParameter"},
+ {0x001000C2, nullptr, "DebugFunc"},
+ {0x001100C0, nullptr, "MapProgramIdForDebug"},
+ {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
+ {0x00130000, nullptr, "GetPreparationState"},
+ {0x00140040, nullptr, "SetPreparationState"},
+ {0x00150140, nullptr, "PrepareToStartApplication"},
+ {0x00160040, nullptr, "PreloadLibraryApplet"},
+ {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
+ {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
+ {0x00190040, nullptr, "PrepareToStartSystemApplet"},
+ {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
+ {0x001B00C4, nullptr, "StartApplication"},
+ {0x001C0000, nullptr, "WakeupApplication"},
+ {0x001D0000, nullptr, "CancelApplication"},
+ {0x001E0084, nullptr, "StartLibraryApplet"},
+ {0x001F0084, nullptr, "StartSystemApplet"},
+ {0x00200044, nullptr, "StartNewestHomeMenu"},
+ {0x00210000, nullptr, "OrderToCloseApplication"},
+ {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00230040, nullptr, "PrepareToJumpToApplication"},
+ {0x00240044, nullptr, "JumpToApplication"},
+ {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
+ {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
+ {0x00270044, nullptr, "CloseApplication"},
+ {0x00280044, nullptr, "CloseLibraryApplet"},
+ {0x00290044, nullptr, "CloseSystemApplet"},
+ {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
+ {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, nullptr, "JumpToHomeMenu"},
+ {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
+ {0x00300044, nullptr, "LeaveResidentApplet"},
+ {0x00310100, nullptr, "PrepareToDoApplicationJump"},
+ {0x00320084, nullptr, "DoApplicationJump"},
+ {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
+ {0x00340084, nullptr, "SendDeliverArg"},
+ {0x00350080, nullptr, "ReceiveDeliverArg"},
+ {0x00360040, nullptr, "LoadSysMenuArg"},
+ {0x00370042, nullptr, "StoreSysMenuArg"},
+ {0x00380040, nullptr, "PreloadResidentApplet"},
+ {0x00390040, nullptr, "PrepareToStartResidentApplet"},
+ {0x003A0044, nullptr, "StartResidentApplet"},
+ {0x003B0040, nullptr, "CancelLibraryApplet"},
+ {0x003C0042, nullptr, "SendDspSleep"},
+ {0x003D0042, nullptr, "SendDspWakeUp"},
+ {0x003E0080, nullptr, "ReplySleepQuery"},
+ {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
+ {0x00400042, nullptr, "SendCaptureBufferInfo"},
+ {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
+ {0x00420080, nullptr, "SleepSystem"},
+ {0x00430040, NotifyToWait, "NotifyToWait"},
+ {0x00440000, GetSharedFont, "GetSharedFont"},
+ {0x00450040, nullptr, "GetWirelessRebootInfo"},
+ {0x00460104, nullptr, "Wrap"},
+ {0x00470104, nullptr, "Unwrap"},
+ {0x00480100, nullptr, "GetProgramInfo"},
+ {0x00490180, nullptr, "Reboot"},
+ {0x004A0040, nullptr, "GetCaptureInfo"},
+ {0x004B00C2, AppletUtility, "AppletUtility"},
+ {0x004C0000, nullptr, "SetFatalErrDispMode"},
+ {0x004D0080, nullptr, "GetAppletProgramInfo"},
+ {0x004E0000, nullptr, "HardwareResetAsync"},
+ {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
+ {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -315,15 +512,15 @@ Interface::Interface() {
file.ReadBytes(shared_font.data(), (size_t)file.GetSize());
// Create shared font memory object
- shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem");
+ shared_font_mem = Kernel::SharedMemory::Create("APT_U:shared_font_mem");
} else {
LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
- shared_font_mem = 0;
+ shared_font_mem = nullptr;
}
- lock_handle = 0;
+ lock = Kernel::Mutex::Create(false, "APT_U:Lock");
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/boss_p.cpp b/src/core/hle/service/boss_p.cpp
new file mode 100644
index 000000000..b3aa6acee
--- /dev/null
+++ b/src/core/hle/service/boss_p.cpp
@@ -0,0 +1,24 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/boss_p.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace BOSS_P
+
+namespace BOSS_P {
+
+// Empty arrays are illegal -- commented out until an entry is added.
+// const Interface::FunctionInfo FunctionTable[] = { };
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/boss_p.h b/src/core/hle/service/boss_p.h
new file mode 100644
index 000000000..71f1e7464
--- /dev/null
+++ b/src/core/hle/service/boss_p.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace BOSS_P
+
+namespace BOSS_P {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "boss:P";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/boss_u.cpp b/src/core/hle/service/boss_u.cpp
index 1820ea7ad..50bb5d426 100644
--- a/src/core/hle/service/boss_u.cpp
+++ b/src/core/hle/service/boss_u.cpp
@@ -19,7 +19,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/cam_u.cpp b/src/core/hle/service/cam_u.cpp
new file mode 100644
index 000000000..cf3b27664
--- /dev/null
+++ b/src/core/hle/service/cam_u.cpp
@@ -0,0 +1,24 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/cam_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CAM_U
+
+namespace CAM_U {
+
+// Empty arrays are illegal -- commented out until an entry is added.
+//const Interface::FunctionInfo FunctionTable[] = { };
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/cam_u.h b/src/core/hle/service/cam_u.h
new file mode 100644
index 000000000..878c20a84
--- /dev/null
+++ b/src/core/hle/service/cam_u.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CAM_U
+
+namespace CAM_U {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "cam:u";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/cecd_s.cpp b/src/core/hle/service/cecd_s.cpp
new file mode 100644
index 000000000..2c707baff
--- /dev/null
+++ b/src/core/hle/service/cecd_s.cpp
@@ -0,0 +1,24 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/cecd_s.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CECD_S
+
+namespace CECD_S {
+
+// Empty arrays are illegal -- commented out until an entry is added.
+//const Interface::FunctionInfo FunctionTable[] = { };
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/cecd_s.h b/src/core/hle/service/cecd_s.h
new file mode 100644
index 000000000..d880d0391
--- /dev/null
+++ b/src/core/hle/service/cecd_s.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CECD_S
+
+namespace CECD_S {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "cecd:s";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/cecd_u.cpp b/src/core/hle/service/cecd_u.cpp
index b7655ef0b..b7ea3a186 100644
--- a/src/core/hle/service/cecd_u.cpp
+++ b/src/core/hle/service/cecd_u.cpp
@@ -18,7 +18,7 @@ namespace CECD_U {
// Interface class
Interface::Interface() {
- //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ //Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index c74527ca4..e818d7bdc 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -110,7 +110,7 @@ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output);
* The config savegame file in the filesystem is not updated.
* @param block_id The id of the block we want to create
* @param size The size of the block we want to create
- * @param flag The flags of the new block
+ * @param flags The flags of the new block
* @param data A pointer containing the data we will write to the new block
* @returns ResultCode indicating the result of the operation, 0 on success
*/
diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp
index 7c1ee8ac3..555b7884a 100644
--- a/src/core/hle/service/cfg/cfg_i.cpp
+++ b/src/core/hle/service/cfg/cfg_i.cpp
@@ -104,7 +104,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp
new file mode 100644
index 000000000..2170894d6
--- /dev/null
+++ b/src/core/hle/service/cfg/cfg_s.cpp
@@ -0,0 +1,98 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/cfg/cfg.h"
+#include "core/hle/service/cfg/cfg_s.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_S
+
+namespace CFG_S {
+
+/**
+ * CFG_S::GetConfigInfoBlk2 service function
+ * Inputs:
+ * 0 : 0x00010082
+ * 1 : Size
+ * 2 : Block ID
+ * 3 : Descriptor for the output buffer
+ * 4 : Output buffer pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void GetConfigInfoBlk2(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 size = cmd_buffer[1];
+ u32 block_id = cmd_buffer[2];
+ u8* data_pointer = Memory::GetPointer(cmd_buffer[4]);
+
+ if (data_pointer == nullptr) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw;
+}
+
+/**
+ * CFG_S::GetConfigInfoBlk8 service function
+ * Inputs:
+ * 0 : 0x04010082
+ * 1 : Size
+ * 2 : Block ID
+ * 3 : Descriptor for the output buffer
+ * 4 : Output buffer pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void GetConfigInfoBlk8(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 size = cmd_buffer[1];
+ u32 block_id = cmd_buffer[2];
+ u8* data_pointer = Memory::GetPointer(cmd_buffer[4]);
+
+ if (data_pointer == nullptr) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw;
+}
+
+/**
+ * CFG_S::UpdateConfigNANDSavegame service function
+ * Inputs:
+ * 0 : 0x04030000
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void UpdateConfigNANDSavegame(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ cmd_buffer[1] = Service::CFG::UpdateConfigNANDSavegame().raw;
+}
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"},
+ {0x00020000, nullptr, "SecureInfoGetRegion"},
+ {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
+ {0x04020082, nullptr, "SetConfigInfoBlk4"},
+ {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
+ {0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
+ {0x04050000, nullptr, "GetLocalFriendCodeSeed"},
+ {0x04060000, nullptr, "SecureInfoGetRegion"},
+ {0x04070000, nullptr, "SecureInfoGetByte101"},
+ {0x04080042, nullptr, "SecureInfoGetSerialNo"},
+ {0x04090000, nullptr, "UpdateConfigBlk00040003"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/cfg/cfg_s.h b/src/core/hle/service/cfg/cfg_s.h
new file mode 100644
index 000000000..d8b67137f
--- /dev/null
+++ b/src/core/hle/service/cfg/cfg_s.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_S
+
+namespace CFG_S {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "cfg:s";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp
index 835620909..1da9f59f6 100644
--- a/src/core/hle/service/cfg/cfg_u.cpp
+++ b/src/core/hle/service/cfg/cfg_u.cpp
@@ -186,7 +186,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
index 3a557efe1..39b00982c 100644
--- a/src/core/hle/service/csnd_snd.cpp
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -29,7 +29,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index d4affdfbf..0f86894a6 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -13,8 +13,8 @@
namespace DSP_DSP {
static u32 read_pipe_count = 0;
-static Handle semaphore_event = 0;
-static Handle interrupt_event = 0;
+static Kernel::SharedPtr<Kernel::Event> semaphore_event;
+static Kernel::SharedPtr<Kernel::Event> interrupt_event;
void SignalInterrupt() {
// TODO(bunnei): This is just a stub, it does not do anything other than signal to the emulated
@@ -23,11 +23,8 @@ void SignalInterrupt() {
// that check the DSP interrupt signal event to run. We should figure out the different types of
// DSP interrupts, and trigger them at the appropriate times.
- if (interrupt_event == 0) {
- LOG_WARNING(Service_DSP, "cannot signal interrupt until DSP event has been created!");
- return;
- }
- Kernel::SignalEvent(interrupt_event);
+ if (interrupt_event != 0)
+ interrupt_event->Signal();
}
/**
@@ -81,8 +78,8 @@ void LoadComponent(Service::Interface* self) {
void GetSemaphoreEventHandle(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- cmd_buff[1] = 0; // No error
- cmd_buff[3] = semaphore_event; // Event handle
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle
LOG_WARNING(Service_DSP, "(STUBBED) called");
}
@@ -99,9 +96,16 @@ void GetSemaphoreEventHandle(Service::Interface* self) {
void RegisterInterruptEvents(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- interrupt_event = static_cast<Handle>(cmd_buff[4]);
+ auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
+ if (evt != nullptr) {
+ interrupt_event = evt;
+ cmd_buff[1] = 0; // No error
+ } else {
+ LOG_ERROR(Service_DSP, "called with invalid handle=%08X", cmd_buff[4]);
- cmd_buff[1] = 0; // No error
+ // TODO(yuriks): An error should be returned from SendSyncRequest, not in the cmdbuf
+ cmd_buff[1] = -1;
+ }
LOG_WARNING(Service_DSP, "(STUBBED) called");
}
@@ -197,11 +201,11 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- semaphore_event = Kernel::CreateEvent(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event");
- interrupt_event = 0;
+ semaphore_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event");
+ interrupt_event = nullptr;
read_pipe_count = 0;
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
index 8c900eabc..962de2170 100644
--- a/src/core/hle/service/err_f.cpp
+++ b/src/core/hle/service/err_f.cpp
@@ -19,7 +19,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/frd_a.cpp b/src/core/hle/service/frd_a.cpp
new file mode 100644
index 000000000..79140a756
--- /dev/null
+++ b/src/core/hle/service/frd_a.cpp
@@ -0,0 +1,24 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/frd_a.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FRD_A
+
+namespace FRD_A {
+
+// Empty arrays are illegal -- commented out until an entry is added.
+// const Interface::FunctionInfo FunctionTable[] = { };
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/frd_a.h b/src/core/hle/service/frd_a.h
new file mode 100644
index 000000000..f068c6108
--- /dev/null
+++ b/src/core/hle/service/frd_a.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FRD_A
+
+namespace FRD_A {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "frd:a";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/frd_u.cpp b/src/core/hle/service/frd_u.cpp
index 021186e57..59faca77a 100644
--- a/src/core/hle/service/frd_u.cpp
+++ b/src/core/hle/service/frd_u.cpp
@@ -27,7 +27,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 958dd9344..ccf132f31 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -43,6 +43,11 @@ const std::string SDCARD_ID = "00000000000000000000000000000000";
namespace Service {
namespace FS {
+// TODO: Verify code
+/// Returned when a function is passed an invalid handle.
+const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::FS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+
// Command to access archive file
enum class FileCommand : u32 {
Dummy1 = 0x000100C6,
@@ -84,7 +89,7 @@ public:
class File : public Kernel::Session {
public:
File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path)
- : path(path), backend(std::move(backend)), priority(0) {
+ : path(path), priority(0), backend(std::move(backend)) {
}
std::string GetName() const override { return "Path: " + path.DebugStr(); }
@@ -158,7 +163,7 @@ public:
case FileCommand::OpenLinkFile:
{
LOG_WARNING(Service_FS, "(STUBBED) File command OpenLinkFile %s", GetName().c_str());
- cmd_buff[3] = GetHandle();
+ cmd_buff[3] = Kernel::g_handle_table.Create(this).ValueOr(INVALID_HANDLE);
break;
}
@@ -280,7 +285,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi
ResultCode CloseArchive(ArchiveHandle handle) {
if (handle_map.erase(handle) == 0)
- return InvalidHandle(ErrorModule::FS);
+ return ERR_INVALID_HANDLE;
else
return RESULT_SUCCESS;
}
@@ -298,10 +303,11 @@ ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, Arc
return RESULT_SUCCESS;
}
-ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
+ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenFileFromArchive(ArchiveHandle archive_handle,
+ const FileSys::Path& path, const FileSys::Mode mode) {
Archive* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return InvalidHandle(ErrorModule::FS);
+ return ERR_INVALID_HANDLE;
std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode);
if (backend == nullptr) {
@@ -309,16 +315,14 @@ ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSy
ErrorSummary::NotFound, ErrorLevel::Status);
}
- auto file = Common::make_unique<File>(std::move(backend), path);
- // TOOD(yuriks): Fix error reporting
- Handle handle = Kernel::g_handle_table.Create(file.release()).ValueOr(INVALID_HANDLE);
- return MakeResult<Handle>(handle);
+ auto file = Kernel::SharedPtr<File>(new File(std::move(backend), path));
+ return MakeResult<Kernel::SharedPtr<Kernel::Session>>(std::move(file));
}
ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
Archive* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return InvalidHandle(ErrorModule::FS);
+ return ERR_INVALID_HANDLE;
if (archive->backend->DeleteFile(path))
return RESULT_SUCCESS;
@@ -331,7 +335,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil
Archive* src_archive = GetArchive(src_archive_handle);
Archive* dest_archive = GetArchive(dest_archive_handle);
if (src_archive == nullptr || dest_archive == nullptr)
- return InvalidHandle(ErrorModule::FS);
+ return ERR_INVALID_HANDLE;
if (src_archive == dest_archive) {
if (src_archive->backend->RenameFile(src_path, dest_path))
@@ -350,7 +354,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil
ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
Archive* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return InvalidHandle(ErrorModule::FS);
+ return ERR_INVALID_HANDLE;
if (archive->backend->DeleteDirectory(path))
return RESULT_SUCCESS;
@@ -361,7 +365,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size) {
Archive* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return InvalidHandle(ErrorModule::FS);
+ return ERR_INVALID_HANDLE;
return archive->backend->CreateFile(path, file_size);
}
@@ -369,7 +373,7 @@ ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path
ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
Archive* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return InvalidHandle(ErrorModule::FS);
+ return ERR_INVALID_HANDLE;
if (archive->backend->CreateDirectory(path))
return RESULT_SUCCESS;
@@ -382,7 +386,7 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons
Archive* src_archive = GetArchive(src_archive_handle);
Archive* dest_archive = GetArchive(dest_archive_handle);
if (src_archive == nullptr || dest_archive == nullptr)
- return InvalidHandle(ErrorModule::FS);
+ return ERR_INVALID_HANDLE;
if (src_archive == dest_archive) {
if (src_archive->backend->RenameDirectory(src_path, dest_path))
@@ -398,16 +402,11 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons
ErrorSummary::NothingHappened, ErrorLevel::Status);
}
-/**
- * Open a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Opened Directory object
- */
-ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenDirectoryFromArchive(ArchiveHandle archive_handle,
+ const FileSys::Path& path) {
Archive* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return InvalidHandle(ErrorModule::FS);
+ return ERR_INVALID_HANDLE;
std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path);
if (backend == nullptr) {
@@ -415,10 +414,8 @@ ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const F
ErrorSummary::NotFound, ErrorLevel::Permanent);
}
- auto directory = Common::make_unique<Directory>(std::move(backend), path);
- // TOOD(yuriks): Fix error reporting
- Handle handle = Kernel::g_handle_table.Create(directory.release()).ValueOr(INVALID_HANDLE);
- return MakeResult<Handle>(handle);
+ auto directory = Kernel::SharedPtr<Directory>(new Directory(std::move(backend), path));
+ return MakeResult<Kernel::SharedPtr<Kernel::Session>>(std::move(directory));
}
ResultCode FormatSaveData() {
@@ -432,6 +429,28 @@ ResultCode FormatSaveData() {
return archive_itr->second->backend->Format(FileSys::Path());
}
+ResultCode CreateExtSaveData(u32 high, u32 low) {
+ // Construct the binary path to the archive first
+ std::vector<u8> binary_path;
+ binary_path.reserve(12);
+ // The first word is all zero to specify a NAND archive
+ for (unsigned i = 0; i < 4; ++i)
+ binary_path.push_back(0);
+ // Next is the low word
+ for (unsigned i = 0; i < 4; ++i)
+ binary_path.push_back((low >> (8 * i)) & 0xFF);
+ // Next is the high word
+ for (unsigned i = 0; i < 4; ++i)
+ binary_path.push_back((high >> i) & 0xFF);
+ FileSys::Path path(binary_path);
+ std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
+ std::string base_path = FileSys::GetExtDataContainerPath(nand_directory, true);
+ std::string extsavedata_path = FileSys::GetExtSaveDataPath(base_path, path);
+ if (!FileUtil::CreateFullPath(extsavedata_path))
+ return ResultCode(-1); // TODO(Subv): Find the right error code
+ return RESULT_SUCCESS;
+}
+
/// Initialize archives
void ArchiveInit() {
next_handle = 1;
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index b3f2134f2..ab5ea4da8 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -15,6 +15,10 @@ extern const std::string SYSTEM_ID;
/// The scrambled SD card CID, also known as ID1
extern const std::string SDCARD_ID;
+namespace Kernel {
+ class Session;
+}
+
namespace Service {
namespace FS {
@@ -42,7 +46,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi
/**
* Closes an archive
- * @param id_code IdCode of the archive to open
+ * @param handle Handle to the archive to close
*/
ResultCode CloseArchive(ArchiveHandle handle);
@@ -58,9 +62,10 @@ ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, Arc
* @param archive_handle Handle to an open Archive object
* @param path Path to the File inside of the Archive
* @param mode Mode under which to open the File
- * @return Handle to the opened File object
+ * @return The opened File object as a Session
*/
-ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode);
+ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenFileFromArchive(ArchiveHandle archive_handle,
+ const FileSys::Path& path, const FileSys::Mode mode);
/**
* Delete a File from an Archive
@@ -121,9 +126,10 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons
* Open a Directory from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the Directory inside of the Archive
- * @return Handle to the opened File object
+ * @return The opened Directory object as a Session
*/
-ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenDirectoryFromArchive(ArchiveHandle archive_handle,
+ const FileSys::Path& path);
/**
* Creates a blank SaveData archive.
@@ -131,6 +137,14 @@ ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const F
*/
ResultCode FormatSaveData();
+/**
+ * Creates a blank SharedExtSaveData archive for the specified extdata ID
+ * @param high The high word of the extdata id to create
+ * @param low The low word of the extdata id to create
+ * @return ResultCode 0 on success or the corresponding code on error
+ */
+ResultCode CreateExtSaveData(u32 high, u32 low);
+
/// Initialize archives
void ArchiveInit();
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 56f3117f4..94a3a31c8 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -14,6 +14,9 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace FS_User
+using Kernel::SharedPtr;
+using Kernel::Session;
+
namespace Service {
namespace FS {
@@ -58,10 +61,10 @@ static void OpenFile(Service::Interface* self) {
LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes);
- ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode);
- cmd_buff[1] = handle.Code().raw;
- if (handle.Succeeded()) {
- cmd_buff[3] = *handle;
+ ResultVal<SharedPtr<Session>> file_res = OpenFileFromArchive(archive_handle, file_path, mode);
+ cmd_buff[1] = file_res.Code().raw;
+ if (file_res.Succeeded()) {
+ cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom();
} else {
cmd_buff[3] = 0;
LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
@@ -114,10 +117,10 @@ static void OpenFileDirectly(Service::Interface* self) {
}
SCOPE_EXIT({ CloseArchive(*archive_handle); });
- ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode);
- cmd_buff[1] = handle.Code().raw;
- if (handle.Succeeded()) {
- cmd_buff[3] = *handle;
+ ResultVal<SharedPtr<Session>> file_res = OpenFileFromArchive(*archive_handle, file_path, mode);
+ cmd_buff[1] = file_res.Code().raw;
+ if (file_res.Succeeded()) {
+ cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom();
} else {
cmd_buff[3] = 0;
LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
@@ -334,10 +337,10 @@ static void OpenDirectory(Service::Interface* self) {
LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
- ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path);
- cmd_buff[1] = handle.Code().raw;
- if (handle.Succeeded()) {
- cmd_buff[3] = *handle;
+ ResultVal<SharedPtr<Session>> dir_res = OpenDirectoryFromArchive(archive_handle, dir_path);
+ cmd_buff[1] = dir_res.Code().raw;
+ if (dir_res.Succeeded()) {
+ cmd_buff[3] = Kernel::g_handle_table.Create(*dir_res).MoveFrom();
} else {
LOG_ERROR(Service_FS, "failed to get a handle for directory");
}
@@ -484,6 +487,15 @@ static void FormatThisUserSaveData(Service::Interface* self) {
cmd_buff[1] = FormatSaveData().raw;
}
+static void CreateExtSaveData(Service::Interface* self) {
+ // TODO(Subv): Figure out the other parameters.
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 save_high = cmd_buff[1];
+ u32 save_low = cmd_buff[2];
+ // TODO(Subv): For now it is assumed that only SharedExtSaveData can be created like this
+ cmd_buff[1] = CreateExtSaveData(save_high, save_low).raw;
+}
+
const FSUserInterface::FunctionInfo FunctionTable[] = {
{0x000100C6, nullptr, "Dummy1"},
{0x040100C4, nullptr, "Control"},
@@ -567,6 +579,8 @@ const FSUserInterface::FunctionInfo FunctionTable[] = {
{0x084E0342, nullptr, "UpdateSha256Context"},
{0x084F0102, nullptr, "ReadSpecialFile"},
{0x08500040, nullptr, "GetSpecialFileSize"},
+ {0x08510242, CreateExtSaveData, "CreateExtSaveData"},
+ {0x08520100, nullptr, "DeleteExtSaveData"},
{0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
{0x08610042, nullptr, "InitializeWithSdkVersion"},
{0x08620040, nullptr, "SetPriority"},
@@ -577,7 +591,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = {
// Interface class
FSUserInterface::FSUserInterface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace FS
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 2b115240f..495c117ee 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -22,13 +22,16 @@ GraphicsDebugger g_debugger;
namespace GSP_GPU {
-Handle g_interrupt_event = 0; ///< Handle to event triggered when GSP interrupt has been signalled
-Handle g_shared_memory = 0; ///< Handle to GSP shared memorys
-u32 g_thread_id = 1; ///< Thread index into interrupt relay queue, 1 is arbitrary
+/// Event triggered when GSP interrupt has been signalled
+Kernel::SharedPtr<Kernel::Event> g_interrupt_event;
+/// GSP shared memoryings
+Kernel::SharedPtr<Kernel::SharedMemory> g_shared_memory;
+/// Thread index into interrupt relay queue, 1 is arbitrary
+u32 g_thread_id = 1;
/// Gets a pointer to a thread command buffer in GSP shared memory
static inline u8* GetCommandBuffer(u32 thread_id) {
- ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, 0x800 + (thread_id * sizeof(CommandBuffer)));
+ ResultVal<u8*> ptr = g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer)));
return ptr.ValueOr(nullptr);
}
@@ -37,13 +40,13 @@ static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_in
// For each thread there are two FrameBufferUpdate fields
u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
- ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
+ ResultVal<u8*> ptr = g_shared_memory->GetPointer(offset);
return reinterpret_cast<FrameBufferUpdate*>(ptr.ValueOr(nullptr));
}
/// Gets a pointer to the interrupt relay queue for a given thread index
static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
- ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, sizeof(InterruptRelayQueue) * thread_id);
+ ResultVal<u8*> ptr = g_shared_memory->GetPointer(sizeof(InterruptRelayQueue) * thread_id);
return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr));
}
@@ -181,16 +184,18 @@ static void FlushDataCache(Service::Interface* self) {
static void RegisterInterruptRelayQueue(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 flags = cmd_buff[1];
- g_interrupt_event = cmd_buff[3];
- g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem");
- _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!");
+ g_interrupt_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[3]);
+ _assert_msg_(GSP, (g_interrupt_event != nullptr), "handle is not valid!");
+ g_shared_memory = Kernel::SharedMemory::Create("GSPSharedMem");
+
+ Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom();
cmd_buff[1] = 0x2A07; // Value verified by 3dmoo team, purpose unknown, but needed for GSP init
cmd_buff[2] = g_thread_id++; // Thread ID
- cmd_buff[4] = g_shared_memory; // GSP shared memory
+ cmd_buff[4] = shmem_handle; // GSP shared memory
- Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct?
+ g_interrupt_event->Signal(); // TODO(bunnei): Is this correct?
}
/**
@@ -204,22 +209,35 @@ void SignalInterrupt(InterruptId interrupt_id) {
LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!");
return;
}
- if (0 == g_shared_memory) {
+ if (nullptr == g_shared_memory) {
LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!");
return;
}
for (int thread_id = 0; thread_id < 0x4; ++thread_id) {
InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(thread_id);
- interrupt_relay_queue->number_interrupts = interrupt_relay_queue->number_interrupts + 1;
-
u8 next = interrupt_relay_queue->index;
next += interrupt_relay_queue->number_interrupts;
next = next % 0x34; // 0x34 is the number of interrupt slots
+ interrupt_relay_queue->number_interrupts += 1;
+
interrupt_relay_queue->slot[next] = interrupt_id;
interrupt_relay_queue->error_code = 0x0; // No error
+
+ // Update framebuffer information if requested
+ // TODO(yuriks): Confirm where this code should be called. It is definitely updated without
+ // executing any GSP commands, only waiting on the event.
+ for (int screen_id = 0; screen_id < 2; ++screen_id) {
+ FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
+
+ if (info->is_dirty) {
+ SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
+ }
+
+ info->is_dirty = false;
+ }
}
- Kernel::SignalEvent(g_interrupt_event);
+ g_interrupt_event->Signal();
}
/// Executes the next GSP command
@@ -269,8 +287,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3);
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2);
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2);
-
- SignalInterrupt(InterruptId::PSC0);
break;
}
@@ -283,22 +299,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.output_size), params.out_buffer_size);
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags);
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1);
-
- // TODO(bunnei): Determine if these interrupts should be signalled here.
- SignalInterrupt(InterruptId::PSC1);
- SignalInterrupt(InterruptId::PPF);
-
- // Update framebuffer information if requested
- for (int screen_id = 0; screen_id < 2; ++screen_id) {
- FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
-
- if (info->is_dirty) {
- SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
- info->framebuffer_info->active_fb = info->framebuffer_info->active_fb ^ 1;
- }
-
- info->is_dirty = false;
- }
break;
}
@@ -389,7 +389,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
g_interrupt_event = 0;
g_shared_memory = 0;
diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h
index 932b6170f..65abb194a 100644
--- a/src/core/hle/service/gsp_gpu.h
+++ b/src/core/hle/service/gsp_gpu.h
@@ -45,21 +45,16 @@ enum class CommandId : u32 {
/// GSP thread interrupt relay queue
struct InterruptRelayQueue {
- union {
- u32 hex;
-
- // Index of last interrupt in the queue
- BitField<0,8,u32> index;
-
- // Number of interrupts remaining to be processed by the userland code
- BitField<8,8,u32> number_interrupts;
-
- // Error code - zero on success, otherwise an error has occurred
- BitField<16,8,u32> error_code;
- };
-
- u32 unk0;
- u32 unk1;
+ // Index of last interrupt in the queue
+ u8 index;
+ // Number of interrupts remaining to be processed by the userland code
+ u8 number_interrupts;
+ // Error code - zero on success, otherwise an error has occurred
+ u8 error_code;
+ u8 padding1;
+
+ u32 missed_PDC0;
+ u32 missed_PDC1;
InterruptId slot[0x34]; ///< Interrupt ID slots
};
diff --git a/src/core/hle/service/gsp_lcd.cpp b/src/core/hle/service/gsp_lcd.cpp
new file mode 100644
index 000000000..d63fa1ee2
--- /dev/null
+++ b/src/core/hle/service/gsp_lcd.cpp
@@ -0,0 +1,26 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+
+#include "common/log.h"
+#include "common/bit_field.h"
+
+#include "core/hle/service/gsp_lcd.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace GSP_LCD
+
+namespace GSP_LCD {
+
+/*const Interface::FunctionInfo FunctionTable[] = {
+};*/
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/gsp_lcd.h b/src/core/hle/service/gsp_lcd.h
new file mode 100644
index 000000000..56b3cfe86
--- /dev/null
+++ b/src/core/hle/service/gsp_lcd.h
@@ -0,0 +1,24 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace GSP_LCD
+
+namespace GSP_LCD {
+
+/// Interface to "gsp::Lcd" service
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "gsp::Lcd";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
new file mode 100644
index 000000000..7cb01729e
--- /dev/null
+++ b/src/core/hle/service/hid/hid.cpp
@@ -0,0 +1,142 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/hid/hid.h"
+
+#include "core/arm/arm_interface.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/hle.h"
+
+namespace Service {
+namespace HID {
+
+Kernel::SharedPtr<Kernel::SharedMemory> g_shared_mem = nullptr;
+
+Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_1;
+Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_2;
+Kernel::SharedPtr<Kernel::Event> g_event_accelerometer;
+Kernel::SharedPtr<Kernel::Event> g_event_gyroscope;
+Kernel::SharedPtr<Kernel::Event> g_event_debug_pad;
+
+// Next Pad state update information
+static PadState next_state = {{0}};
+static u32 next_index = 0;
+static s16 next_circle_x = 0;
+static s16 next_circle_y = 0;
+
+/**
+ * Gets a pointer to the PadData structure inside HID shared memory
+ */
+static inline PadData* GetPadData() {
+ if (g_shared_mem == nullptr)
+ return nullptr;
+ return reinterpret_cast<PadData*>(g_shared_mem->GetPointer().ValueOr(nullptr));
+}
+
+/**
+ * Circle Pad from keys.
+ *
+ * This is implemented as "pushed all the way to an edge (max) or centered (0)".
+ *
+ * Indicate the circle pad is pushed completely to the edge in 1 of 8 directions.
+ */
+static void UpdateNextCirclePadState() {
+ static const s16 max_value = 0x9C;
+ next_circle_x = next_state.circle_left ? -max_value : 0x0;
+ next_circle_x += next_state.circle_right ? max_value : 0x0;
+ next_circle_y = next_state.circle_down ? -max_value : 0x0;
+ next_circle_y += next_state.circle_up ? max_value : 0x0;
+}
+
+/**
+ * Sets a Pad state (button or button combo) as pressed
+ */
+void PadButtonPress(const PadState& pad_state) {
+ next_state.hex |= pad_state.hex;
+ UpdateNextCirclePadState();
+}
+
+/**
+ * Sets a Pad state (button or button combo) as released
+ */
+void PadButtonRelease(const PadState& pad_state) {
+ next_state.hex &= ~pad_state.hex;
+ UpdateNextCirclePadState();
+}
+
+/**
+ * Called after all Pad changes to be included in this update have been made,
+ * including both Pad key changes and analog circle Pad changes.
+ */
+void PadUpdateComplete() {
+ PadData* pad_data = GetPadData();
+
+ if (pad_data == nullptr) {
+ return;
+ }
+
+ // Update PadData struct
+ pad_data->current_state.hex = next_state.hex;
+ pad_data->index = next_index;
+ next_index = (next_index + 1) % pad_data->entries.size();
+
+ // Get the previous Pad state
+ u32 last_entry_index = (pad_data->index - 1) % pad_data->entries.size();
+ PadState old_state = pad_data->entries[last_entry_index].current_state;
+
+ // Compute bitmask with 1s for bits different from the old state
+ PadState changed;
+ changed.hex = (next_state.hex ^ old_state.hex);
+
+ // Compute what was added
+ PadState additions;
+ additions.hex = changed.hex & next_state.hex;
+
+ // Compute what was removed
+ PadState removals;
+ removals.hex = changed.hex & old_state.hex;
+
+ // Get the current Pad entry
+ PadDataEntry* current_pad_entry = &pad_data->entries[pad_data->index];
+
+ // Update entry properties
+ current_pad_entry->current_state.hex = next_state.hex;
+ current_pad_entry->delta_additions.hex = additions.hex;
+ current_pad_entry->delta_removals.hex = removals.hex;
+
+ // Set circle Pad
+ current_pad_entry->circle_pad_x = next_circle_x;
+ current_pad_entry->circle_pad_y = next_circle_y;
+
+ // If we just updated index 0, provide a new timestamp
+ if (pad_data->index == 0) {
+ pad_data->index_reset_ticks_previous = pad_data->index_reset_ticks;
+ pad_data->index_reset_ticks = (s64)Core::g_app_core->GetTicks();
+ }
+
+ // Signal both handles when there's an update to Pad or touch
+ g_event_pad_or_touch_1->Signal();
+ g_event_pad_or_touch_2->Signal();
+}
+
+void HIDInit() {
+ using namespace Kernel;
+
+ g_shared_mem = SharedMemory::Create("HID:SharedMem");
+
+ // Create event handles
+ g_event_pad_or_touch_1 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch1");
+ g_event_pad_or_touch_2 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch2");
+ g_event_accelerometer = Event::Create(RESETTYPE_ONESHOT, "HID:EventAccelerometer");
+ g_event_gyroscope = Event::Create(RESETTYPE_ONESHOT, "HID:EventGyroscope");
+ g_event_debug_pad = Event::Create(RESETTYPE_ONESHOT, "HID:EventDebugPad");
+}
+
+void HIDShutdown() {
+
+}
+
+}
+}
diff --git a/src/core/hle/service/hid_user.h b/src/core/hle/service/hid/hid.h
index 5b96dda60..2116d2ca3 100644
--- a/src/core/hle/service/hid_user.h
+++ b/src/core/hle/service/hid/hid.h
@@ -1,19 +1,31 @@
-// Copyright 2014 Citra Emulator Project
+// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
-#include "core/hle/service/service.h"
+#include <array>
+
+#include "core/hle/kernel/kernel.h"
#include "common/bit_field.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace HID_User
+namespace Kernel {
+ class SharedMemory;
+ class Event;
+}
+
+namespace Service {
+namespace HID {
-// This service is used for interfacing to physical user controls.
-// Uses include game pad controls, touchscreen, accelerometers, gyroscopes, and debug pad.
+// Handle to shared memory region designated to HID_User service
+extern Kernel::SharedPtr<Kernel::SharedMemory> g_shared_mem;
-namespace HID_User {
+// Event handles
+extern Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_1;
+extern Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_2;
+extern Kernel::SharedPtr<Kernel::Event> g_event_accelerometer;
+extern Kernel::SharedPtr<Kernel::Event> g_event_gyroscope;
+extern Kernel::SharedPtr<Kernel::Event> g_event_debug_pad;
/**
* Structure of a Pad controller state.
@@ -97,16 +109,8 @@ void PadButtonPress(const PadState& pad_state);
void PadButtonRelease(const PadState& pad_state);
void PadUpdateComplete();
-/**
- * HID service interface.
- */
-class Interface : public Service::Interface {
-public:
- Interface();
-
- std::string GetPortName() const override {
- return "hid:USER";
- }
-};
+void HIDInit();
+void HIDShutdown();
-} // namespace
+}
+}
diff --git a/src/core/hle/service/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp
new file mode 100644
index 000000000..054aa8b59
--- /dev/null
+++ b/src/core/hle/service/hid/hid_spvr.cpp
@@ -0,0 +1,38 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/hid/hid_spvr.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace HID_SPVR
+
+namespace HID_User {
+ extern void GetIPCHandles(Service::Interface* self);
+}
+
+namespace HID_SPVR {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000A0000, HID_User::GetIPCHandles, "GetIPCHandles"},
+ {0x000B0000, nullptr, "StartAnalogStickCalibration"},
+ {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
+ {0x00110000, nullptr, "EnableAccelerometer"},
+ {0x00120000, nullptr, "DisableAccelerometer"},
+ {0x00130000, nullptr, "EnableGyroscopeLow"},
+ {0x00140000, nullptr, "DisableGyroscopeLow"},
+ {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
+ {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
+ {0x00170000, nullptr, "GetSoundVolume"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/hid/hid_spvr.h b/src/core/hle/service/hid/hid_spvr.h
new file mode 100644
index 000000000..53ddc8569
--- /dev/null
+++ b/src/core/hle/service/hid/hid_spvr.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace HID_SPVR
+
+namespace HID_SPVR {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "hid:SPVR";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp
new file mode 100644
index 000000000..68edafebb
--- /dev/null
+++ b/src/core/hle/service/hid/hid_user.cpp
@@ -0,0 +1,78 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+
+#include "core/hle/hle.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/service/hid/hid.h"
+#include "hid_user.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace HID_User
+
+namespace HID_User {
+
+
+// TODO(peachum):
+// Add a method for setting analog input from joystick device for the circle Pad.
+//
+// This method should:
+// * Be called after both PadButton<Press, Release>().
+// * Be called before PadUpdateComplete()
+// * Set current PadEntry.circle_pad_<axis> using analog data
+// * Set PadData.raw_circle_pad_data
+// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_x >= 41
+// * Set PadData.current_state.circle_up = 1 if current PadEntry.circle_pad_y >= 41
+// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41
+// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41
+
+
+/**
+ * HID_User::GetIPCHandles service function
+ * Inputs:
+ * None
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Unused
+ * 3 : Handle to HID_User shared memory
+ * 4 : Event signaled by HID_User
+ * 5 : Event signaled by HID_User
+ * 6 : Event signaled by HID_User
+ * 7 : Gyroscope event
+ * 8 : Event signaled by HID_User
+ */
+void GetIPCHandles(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0; // No error
+ // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling)
+ cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::g_shared_mem).MoveFrom();
+ cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_1).MoveFrom();
+ cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_2).MoveFrom();
+ cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::g_event_accelerometer).MoveFrom();
+ cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::g_event_gyroscope).MoveFrom();
+ cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::g_event_debug_pad).MoveFrom();
+}
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000A0000, GetIPCHandles, "GetIPCHandles"},
+ {0x00110000, nullptr, "EnableAccelerometer"},
+ {0x00120000, nullptr, "DisableAccelerometer"},
+ {0x00130000, nullptr, "EnableGyroscopeLow"},
+ {0x00140000, nullptr, "DisableGyroscopeLow"},
+ {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
+ {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
+ {0x00170000, nullptr, "GetSoundVolume"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/hid/hid_user.h b/src/core/hle/service/hid/hid_user.h
new file mode 100644
index 000000000..1d9929e67
--- /dev/null
+++ b/src/core/hle/service/hid/hid_user.h
@@ -0,0 +1,29 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace HID_User
+
+// This service is used for interfacing to physical user controls.
+// Uses include game pad controls, touchscreen, accelerometers, gyroscopes, and debug pad.
+
+namespace HID_User {
+
+/**
+ * HID service interface.
+ */
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "hid:USER";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp
deleted file mode 100644
index 1403b1de9..000000000
--- a/src/core/hle/service/hid_user.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/log.h"
-
-#include "core/arm/arm_interface.h"
-#include "core/hle/hle.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/shared_memory.h"
-#include "hid_user.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace HID_User
-
-namespace HID_User {
-
-// Handle to shared memory region designated to HID_User service
-static Handle shared_mem = 0;
-
-// Event handles
-static Handle event_pad_or_touch_1 = 0;
-static Handle event_pad_or_touch_2 = 0;
-static Handle event_accelerometer = 0;
-static Handle event_gyroscope = 0;
-static Handle event_debug_pad = 0;
-
-// Next Pad state update information
-static PadState next_state = {{0}};
-static u32 next_index = 0;
-static s16 next_circle_x = 0;
-static s16 next_circle_y = 0;
-
-/**
- * Gets a pointer to the PadData structure inside HID shared memory
- */
-static inline PadData* GetPadData() {
- return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0).ValueOr(nullptr));
-}
-
-/**
- * Circle Pad from keys.
- *
- * This is implemented as "pushed all the way to an edge (max) or centered (0)".
- *
- * Indicate the circle pad is pushed completely to the edge in 1 of 8 directions.
- */
-static void UpdateNextCirclePadState() {
- static const s16 max_value = 0x9C;
- next_circle_x = next_state.circle_left ? -max_value : 0x0;
- next_circle_x += next_state.circle_right ? max_value : 0x0;
- next_circle_y = next_state.circle_down ? -max_value : 0x0;
- next_circle_y += next_state.circle_up ? max_value : 0x0;
-}
-
-/**
- * Sets a Pad state (button or button combo) as pressed
- */
-void PadButtonPress(const PadState& pad_state) {
- next_state.hex |= pad_state.hex;
- UpdateNextCirclePadState();
-}
-
-/**
- * Sets a Pad state (button or button combo) as released
- */
-void PadButtonRelease(const PadState& pad_state) {
- next_state.hex &= ~pad_state.hex;
- UpdateNextCirclePadState();
-}
-
-/**
- * Called after all Pad changes to be included in this update have been made,
- * including both Pad key changes and analog circle Pad changes.
- */
-void PadUpdateComplete() {
- PadData* pad_data = GetPadData();
-
- if (pad_data == nullptr) {
- return;
- }
-
- // Update PadData struct
- pad_data->current_state.hex = next_state.hex;
- pad_data->index = next_index;
- next_index = (next_index + 1) % pad_data->entries.size();
-
- // Get the previous Pad state
- u32 last_entry_index = (pad_data->index - 1) % pad_data->entries.size();
- PadState old_state = pad_data->entries[last_entry_index].current_state;
-
- // Compute bitmask with 1s for bits different from the old state
- PadState changed;
- changed.hex = (next_state.hex ^ old_state.hex);
-
- // Compute what was added
- PadState additions;
- additions.hex = changed.hex & next_state.hex;
-
- // Compute what was removed
- PadState removals;
- removals.hex = changed.hex & old_state.hex;
-
- // Get the current Pad entry
- PadDataEntry* current_pad_entry = &pad_data->entries[pad_data->index];
-
- // Update entry properties
- current_pad_entry->current_state.hex = next_state.hex;
- current_pad_entry->delta_additions.hex = additions.hex;
- current_pad_entry->delta_removals.hex = removals.hex;
-
- // Set circle Pad
- current_pad_entry->circle_pad_x = next_circle_x;
- current_pad_entry->circle_pad_y = next_circle_y;
-
- // If we just updated index 0, provide a new timestamp
- if (pad_data->index == 0) {
- pad_data->index_reset_ticks_previous = pad_data->index_reset_ticks;
- pad_data->index_reset_ticks = (s64)Core::g_app_core->GetTicks();
- }
-
- // Signal both handles when there's an update to Pad or touch
- Kernel::SignalEvent(event_pad_or_touch_1);
- Kernel::SignalEvent(event_pad_or_touch_2);
-}
-
-
-// TODO(peachum):
-// Add a method for setting analog input from joystick device for the circle Pad.
-//
-// This method should:
-// * Be called after both PadButton<Press, Release>().
-// * Be called before PadUpdateComplete()
-// * Set current PadEntry.circle_pad_<axis> using analog data
-// * Set PadData.raw_circle_pad_data
-// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_x >= 41
-// * Set PadData.current_state.circle_up = 1 if current PadEntry.circle_pad_y >= 41
-// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41
-// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41
-
-
-/**
- * HID_User::GetIPCHandles service function
- * Inputs:
- * None
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : Unused
- * 3 : Handle to HID_User shared memory
- * 4 : Event signaled by HID_User
- * 5 : Event signaled by HID_User
- * 6 : Event signaled by HID_User
- * 7 : Gyroscope event
- * 8 : Event signaled by HID_User
- */
-static void GetIPCHandles(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
-
- cmd_buff[1] = 0; // No error
- cmd_buff[3] = shared_mem;
- cmd_buff[4] = event_pad_or_touch_1;
- cmd_buff[5] = event_pad_or_touch_2;
- cmd_buff[6] = event_accelerometer;
- cmd_buff[7] = event_gyroscope;
- cmd_buff[8] = event_debug_pad;
-}
-
-const Interface::FunctionInfo FunctionTable[] = {
- {0x000A0000, GetIPCHandles, "GetIPCHandles"},
- {0x000B0000, nullptr, "StartAnalogStickCalibration"},
- {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
- {0x00110000, nullptr, "EnableAccelerometer"},
- {0x00120000, nullptr, "DisableAccelerometer"},
- {0x00130000, nullptr, "EnableGyroscopeLow"},
- {0x00140000, nullptr, "DisableGyroscopeLow"},
- {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"},
- {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"},
- {0x00170000, nullptr, "GetSoundVolume"},
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
- shared_mem = Kernel::CreateSharedMemory("HID_User:SharedMem"); // Create shared memory object
-
- // Create event handles
- event_pad_or_touch_1 = Kernel::CreateEvent(RESETTYPE_ONESHOT, "HID_User:EventPadOrTouch1");
- event_pad_or_touch_2 = Kernel::CreateEvent(RESETTYPE_ONESHOT, "HID_User:EventPadOrTouch2");
- event_accelerometer = Kernel::CreateEvent(RESETTYPE_ONESHOT, "HID_User:EventAccelerometer");
- event_gyroscope = Kernel::CreateEvent(RESETTYPE_ONESHOT, "HID_User:EventGyroscope");
- event_debug_pad = Kernel::CreateEvent(RESETTYPE_ONESHOT, "HID_User:EventDebugPad");
-
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
-}
-
-} // namespace
diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp
index d0bff552f..6595ca572 100644
--- a/src/core/hle/service/http_c.cpp
+++ b/src/core/hle/service/http_c.cpp
@@ -58,7 +58,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/ir_rst.cpp b/src/core/hle/service/ir_rst.cpp
index d49bd5335..31da8e160 100644
--- a/src/core/hle/service/ir_rst.cpp
+++ b/src/core/hle/service/ir_rst.cpp
@@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/ir_u.cpp b/src/core/hle/service/ir_u.cpp
index da6f38e41..7fa233048 100644
--- a/src/core/hle/service/ir_u.cpp
+++ b/src/core/hle/service/ir_u.cpp
@@ -36,7 +36,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp
index 7d6e2e8e8..459717fff 100644
--- a/src/core/hle/service/ldr_ro.cpp
+++ b/src/core/hle/service/ldr_ro.cpp
@@ -27,7 +27,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index 82bce9180..af967b5b6 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -34,7 +34,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/ndm_u.cpp b/src/core/hle/service/ndm_u.cpp
index 0f03de6ae..df3c97193 100644
--- a/src/core/hle/service/ndm_u.cpp
+++ b/src/core/hle/service/ndm_u.cpp
@@ -24,7 +24,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/news_s.cpp b/src/core/hle/service/news_s.cpp
new file mode 100644
index 000000000..d7537875b
--- /dev/null
+++ b/src/core/hle/service/news_s.cpp
@@ -0,0 +1,25 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/news_s.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NEWS_S
+
+namespace NEWS_S {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000100C6, nullptr, "AddNotification"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/news_s.h b/src/core/hle/service/news_s.h
new file mode 100644
index 000000000..f8b4636d5
--- /dev/null
+++ b/src/core/hle/service/news_s.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NEWS_S
+
+namespace NEWS_S {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "news:s";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/news_u.cpp b/src/core/hle/service/news_u.cpp
index b5adad4c6..a9e161c23 100644
--- a/src/core/hle/service/news_u.cpp
+++ b/src/core/hle/service/news_u.cpp
@@ -19,7 +19,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/nim_aoc.cpp b/src/core/hle/service/nim_aoc.cpp
index 17d1c4ff5..ab2ef4429 100644
--- a/src/core/hle/service/nim_aoc.cpp
+++ b/src/core/hle/service/nim_aoc.cpp
@@ -25,7 +25,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/ns_s.cpp b/src/core/hle/service/ns_s.cpp
new file mode 100644
index 000000000..5cf3e2039
--- /dev/null
+++ b/src/core/hle/service/ns_s.cpp
@@ -0,0 +1,27 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+
+#include "common/common.h"
+
+#include "core/hle/hle.h"
+#include "core/hle/service/ns_s.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NS_S
+
+namespace NS_S {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000200C0, nullptr, "LaunchTitle"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/ns_s.h b/src/core/hle/service/ns_s.h
new file mode 100644
index 000000000..8d8e849b8
--- /dev/null
+++ b/src/core/hle/service/ns_s.h
@@ -0,0 +1,24 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NS_S
+
+namespace NS_S {
+
+/// Interface to "NS:S" service
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "ns:s";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp
index ce456a966..61fcb54ce 100644
--- a/src/core/hle/service/nwm_uds.cpp
+++ b/src/core/hle/service/nwm_uds.cpp
@@ -26,7 +26,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp
index 529dccafb..d61eaf80f 100644
--- a/src/core/hle/service/pm_app.cpp
+++ b/src/core/hle/service/pm_app.cpp
@@ -26,7 +26,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/ptm_play.cpp b/src/core/hle/service/ptm_play.cpp
new file mode 100644
index 000000000..b357057fd
--- /dev/null
+++ b/src/core/hle/service/ptm_play.cpp
@@ -0,0 +1,28 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ptm_play.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PTM_PLAY
+
+namespace PTM_PLAY {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ { 0x08070082, nullptr, "GetPlayHistory" },
+ { 0x08080000, nullptr, "GetPlayHistoryStart" },
+ { 0x08090000, nullptr, "GetPlayHistoryLength" },
+ { 0x080B0080, nullptr, "CalcPlayHistoryStart" },
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/ptm_play.h b/src/core/hle/service/ptm_play.h
new file mode 100644
index 000000000..2f4f0d6fd
--- /dev/null
+++ b/src/core/hle/service/ptm_play.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PTM_PLAY
+
+namespace PTM_PLAY {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+std::string GetPortName() const override {
+ return "ptm:play";
+}
+};
+
+} // namespace
diff --git a/src/core/hle/service/ptm_sysm.cpp b/src/core/hle/service/ptm_sysm.cpp
new file mode 100644
index 000000000..b6f688de3
--- /dev/null
+++ b/src/core/hle/service/ptm_sysm.cpp
@@ -0,0 +1,56 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "common/make_unique.h"
+#include "core/file_sys/archive_extsavedata.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ptm_sysm.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PTM_SYSM
+
+namespace PTM_SYSM {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x040100C0, nullptr, "SetRtcAlarmEx"},
+ {0x04020042, nullptr, "ReplySleepQuery"},
+ {0x04030042, nullptr, "NotifySleepPreparationComplete"},
+ {0x04040102, nullptr, "SetWakeupTrigger"},
+ {0x04050000, nullptr, "GetAwakeReason"},
+ {0x04060000, nullptr, "RequestSleep"},
+ {0x040700C0, nullptr, "ShutdownAsync"},
+ {0x04080000, nullptr, "Awake"},
+ {0x04090080, nullptr, "RebootAsync"},
+ {0x040A0000, nullptr, "CheckNew3DS"},
+ {0x08010640, nullptr, "SetInfoLEDPattern"},
+ {0x08020040, nullptr, "SetInfoLEDPatternHeader"},
+ {0x08030000, nullptr, "GetInfoLEDStatus"},
+ {0x08040040, nullptr, "SetBatteryEmptyLEDPattern"},
+ {0x08050000, nullptr, "ClearStepHistory"},
+ {0x080600C2, nullptr, "SetStepHistory"},
+ {0x08070082, nullptr, "GetPlayHistory"},
+ {0x08080000, nullptr, "GetPlayHistoryStart"},
+ {0x08090000, nullptr, "GetPlayHistoryLength"},
+ {0x080A0000, nullptr, "ClearPlayHistory"},
+ {0x080B0080, nullptr, "CalcPlayHistoryStart"},
+ {0x080C0080, nullptr, "SetUserTime"},
+ {0x080D0000, nullptr, "InvalidateSystemTime"},
+ {0x080E0140, nullptr, "NotifyPlayEvent"},
+ {0x080F0000, nullptr, "IsLegacyPowerOff"},
+ {0x08100000, nullptr, "ClearLegacyPowerOff"},
+ {0x08110000, nullptr, "GetShellStatus"},
+ {0x08120000, nullptr, "IsShutdownByBatteryEmpty"},
+ {0x08130000, nullptr, "FormatSavedata"},
+ {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"}
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace
diff --git a/src/core/hle/service/ptm_sysm.h b/src/core/hle/service/ptm_sysm.h
new file mode 100644
index 000000000..0f267b214
--- /dev/null
+++ b/src/core/hle/service/ptm_sysm.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PTM_SYSM
+
+namespace PTM_SYSM {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "ptm:sysm";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp
index 753180add..7b465a348 100644
--- a/src/core/hle/service/ptm_u.cpp
+++ b/src/core/hle/service/ptm_u.cpp
@@ -137,7 +137,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
// Create the SharedExtSaveData archive 0xF000000B and the gamecoin.dat file
// TODO(Subv): In the future we should use the FS service to query this archive
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 33c29a4a0..e0979ea5d 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -10,30 +10,43 @@
#include "core/hle/service/act_u.h"
#include "core/hle/service/am_app.h"
#include "core/hle/service/am_net.h"
+#include "core/hle/service/am_sys.h"
#include "core/hle/service/apt_a.h"
+#include "core/hle/service/apt_s.h"
#include "core/hle/service/apt_u.h"
+#include "core/hle/service/boss_p.h"
#include "core/hle/service/boss_u.h"
+#include "core/hle/service/cam_u.h"
#include "core/hle/service/cecd_u.h"
+#include "core/hle/service/cecd_s.h"
#include "core/hle/service/cfg/cfg_i.h"
+#include "core/hle/service/cfg/cfg_s.h"
#include "core/hle/service/cfg/cfg_u.h"
#include "core/hle/service/csnd_snd.h"
#include "core/hle/service/dsp_dsp.h"
#include "core/hle/service/err_f.h"
#include "core/hle/service/fs/fs_user.h"
+#include "core/hle/service/frd_a.h"
#include "core/hle/service/frd_u.h"
#include "core/hle/service/gsp_gpu.h"
-#include "core/hle/service/hid_user.h"
+#include "core/hle/service/hid/hid_spvr.h"
+#include "core/hle/service/hid/hid_user.h"
+#include "core/hle/service/gsp_lcd.h"
#include "core/hle/service/http_c.h"
#include "core/hle/service/ir_rst.h"
#include "core/hle/service/ir_u.h"
#include "core/hle/service/ldr_ro.h"
#include "core/hle/service/mic_u.h"
#include "core/hle/service/ndm_u.h"
+#include "core/hle/service/news_s.h"
#include "core/hle/service/news_u.h"
#include "core/hle/service/nim_aoc.h"
+#include "core/hle/service/ns_s.h"
#include "core/hle/service/nwm_uds.h"
#include "core/hle/service/pm_app.h"
+#include "core/hle/service/ptm_play.h"
#include "core/hle/service/ptm_u.h"
+#include "core/hle/service/ptm_sysm.h"
#include "core/hle/service/soc_u.h"
#include "core/hle/service/srv.h"
#include "core/hle/service/ssl_c.h"
@@ -41,83 +54,76 @@
namespace Service {
-Manager* g_manager = nullptr; ///< Service manager
+std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports;
+std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services;
////////////////////////////////////////////////////////////////////////////////////////////////////
-// Service Manager class
-
-void Manager::AddService(Interface* service) {
- // TOOD(yuriks): Fix error reporting
- m_port_map[service->GetPortName()] = Kernel::g_handle_table.Create(service).ValueOr(INVALID_HANDLE);
- m_services.push_back(service);
-}
-
-void Manager::DeleteService(const std::string& port_name) {
- Interface* service = FetchFromPortName(port_name);
- m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end());
- m_port_map.erase(port_name);
-}
+// Module interface
-Interface* Manager::FetchFromHandle(Handle handle) {
- // TODO(yuriks): This function is very suspicious and should probably be exterminated.
- return Kernel::g_handle_table.Get<Interface>(handle).get();
+static void AddNamedPort(Interface* interface) {
+ g_kernel_named_ports.emplace(interface->GetPortName(), interface);
}
-Interface* Manager::FetchFromPortName(const std::string& port_name) {
- auto itr = m_port_map.find(port_name);
- if (itr == m_port_map.end()) {
- return nullptr;
- }
- return FetchFromHandle(itr->second);
+static void AddService(Interface* interface) {
+ g_srv_services.emplace(interface->GetPortName(), interface);
}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Module interface
-
/// Initialize ServiceManager
void Init() {
- g_manager = new Manager;
-
- g_manager->AddService(new SRV::Interface);
- g_manager->AddService(new AC_U::Interface);
- g_manager->AddService(new ACT_U::Interface);
- g_manager->AddService(new AM_APP::Interface);
- g_manager->AddService(new AM_NET::Interface);
- g_manager->AddService(new APT_A::Interface);
- g_manager->AddService(new APT_U::Interface);
- g_manager->AddService(new BOSS_U::Interface);
- g_manager->AddService(new CECD_U::Interface);
- g_manager->AddService(new CFG_I::Interface);
- g_manager->AddService(new CFG_U::Interface);
- g_manager->AddService(new CSND_SND::Interface);
- g_manager->AddService(new DSP_DSP::Interface);
- g_manager->AddService(new ERR_F::Interface);
- g_manager->AddService(new FRD_U::Interface);
- g_manager->AddService(new FS::FSUserInterface);
- g_manager->AddService(new GSP_GPU::Interface);
- g_manager->AddService(new HID_User::Interface);
- g_manager->AddService(new HTTP_C::Interface);
- g_manager->AddService(new IR_RST::Interface);
- g_manager->AddService(new IR_U::Interface);
- g_manager->AddService(new LDR_RO::Interface);
- g_manager->AddService(new MIC_U::Interface);
- g_manager->AddService(new NDM_U::Interface);
- g_manager->AddService(new NEWS_U::Interface);
- g_manager->AddService(new NIM_AOC::Interface);
- g_manager->AddService(new NWM_UDS::Interface);
- g_manager->AddService(new PM_APP::Interface);
- g_manager->AddService(new PTM_U::Interface);
- g_manager->AddService(new SOC_U::Interface);
- g_manager->AddService(new SSL_C::Interface);
- g_manager->AddService(new Y2R_U::Interface);
+ AddNamedPort(new SRV::Interface);
+
+ AddService(new AC_U::Interface);
+ AddService(new ACT_U::Interface);
+ AddService(new AM_APP::Interface);
+ AddService(new AM_NET::Interface);
+ AddService(new AM_SYS::Interface);
+ AddService(new APT_A::Interface);
+ AddService(new APT_S::Interface);
+ AddService(new APT_U::Interface);
+ AddService(new BOSS_P::Interface);
+ AddService(new BOSS_U::Interface);
+ AddService(new CAM_U::Interface);
+ AddService(new CECD_S::Interface);
+ AddService(new CECD_U::Interface);
+ AddService(new CFG_I::Interface);
+ AddService(new CFG_S::Interface);
+ AddService(new CFG_U::Interface);
+ AddService(new CSND_SND::Interface);
+ AddService(new DSP_DSP::Interface);
+ AddService(new ERR_F::Interface);
+ AddService(new FRD_A::Interface);
+ AddService(new FRD_U::Interface);
+ AddService(new FS::FSUserInterface);
+ AddService(new GSP_GPU::Interface);
+ AddService(new GSP_LCD::Interface);
+ AddService(new HID_User::Interface);
+ AddService(new HID_SPVR::Interface);
+ AddService(new HTTP_C::Interface);
+ AddService(new IR_RST::Interface);
+ AddService(new IR_U::Interface);
+ AddService(new LDR_RO::Interface);
+ AddService(new MIC_U::Interface);
+ AddService(new NDM_U::Interface);
+ AddService(new NEWS_S::Interface);
+ AddService(new NEWS_U::Interface);
+ AddService(new NIM_AOC::Interface);
+ AddService(new NS_S::Interface);
+ AddService(new NWM_UDS::Interface);
+ AddService(new PM_APP::Interface);
+ AddService(new PTM_PLAY::Interface);
+ AddService(new PTM_U::Interface);
+ AddService(new PTM_SYSM::Interface);
+ AddService(new SOC_U::Interface);
+ AddService(new SSL_C::Interface);
+ AddService(new Y2R_U::Interface);
LOG_DEBUG(Service, "initialized OK");
}
/// Shutdown ServiceManager
void Shutdown() {
- delete g_manager;
+ g_srv_services.clear();
+ g_kernel_named_ports.clear();
LOG_DEBUG(Service, "shutdown OK");
}
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index e75d5008b..3370f9f9b 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -5,9 +5,11 @@
#pragma once
#include <algorithm>
-#include <vector>
-#include <map>
#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <boost/container/flat_map.hpp>
#include "common/common.h"
#include "common/string_util.h"
@@ -27,7 +29,7 @@ static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 character
class Manager;
/// Interface to a CTROS service
-class Interface : public Kernel::Session {
+class Interface : public Kernel::Session {
// TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be
// just something that encapsulates a session and acts as a helper to implement service
// processes.
@@ -38,11 +40,11 @@ class Interface : public Kernel::Session {
* Creates a function string for logging, complete with the name (or header code, depending
* on what's passed in) the port name, and all the cmd_buff arguments.
*/
- std::string MakeFunctionString(const std::string& name, const std::string& port_name, const u32* cmd_buff) {
+ std::string MakeFunctionString(const char* name, const char* port_name, const u32* cmd_buff) {
// Number of params == bits 0-5 + bits 6-11
int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
- std::string function_string = Common::StringFromFormat("function '%s': port=%s", name.c_str(), port_name.c_str());
+ std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name);
for (int i = 1; i <= num_params; ++i) {
function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
}
@@ -57,7 +59,7 @@ public:
struct FunctionInfo {
u32 id;
Function func;
- std::string name;
+ const char* name;
};
/**
@@ -68,34 +70,19 @@ public:
return "[UNKNOWN SERVICE PORT]";
}
- /// Allocates a new handle for the service
- Handle CreateHandle(Kernel::Object *obj) {
- // TODO(yuriks): Fix error reporting
- Handle handle = Kernel::g_handle_table.Create(obj).ValueOr(INVALID_HANDLE);
- m_handles.push_back(handle);
- return handle;
- }
-
- /// Frees a handle from the service
- template <class T>
- void DeleteHandle(const Handle handle) {
- Kernel::g_handle_table.Close(handle);
- m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end());
- }
-
ResultVal<bool> SyncRequest() override {
u32* cmd_buff = Kernel::GetCommandBuffer();
auto itr = m_functions.find(cmd_buff[0]);
if (itr == m_functions.end() || itr->second.func == nullptr) {
std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
- LOG_ERROR(Service, "%s %s", "unknown/unimplemented", MakeFunctionString(function_name, GetPortName(), cmd_buff).c_str());
+ LOG_ERROR(Service, "unknown / unimplemented %s", MakeFunctionString(function_name.c_str(), GetPortName().c_str(), cmd_buff).c_str());
// TODO(bunnei): Hack - ignore error
cmd_buff[1] = 0;
return MakeResult<bool>(false);
} else {
- LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName(), cmd_buff).c_str());
+ LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str());
}
itr->second.func(this);
@@ -108,37 +95,18 @@ protected:
/**
* Registers the functions in the service
*/
- void Register(const FunctionInfo* functions, int len) {
- for (int i = 0; i < len; i++) {
- m_functions[functions[i].id] = functions[i];
+ template <size_t N>
+ void Register(const FunctionInfo (&functions)[N]) {
+ m_functions.reserve(N);
+ for (auto& fn : functions) {
+ // Usually this array is sorted by id already, so hint to instead at the end
+ m_functions.emplace_hint(m_functions.cend(), fn.id, fn);
}
}
private:
+ boost::container::flat_map<u32, FunctionInfo> m_functions;
- std::vector<Handle> m_handles;
- std::map<u32, FunctionInfo> m_functions;
-
-};
-
-/// Simple class to manage accessing services from ports and UID handles
-class Manager {
-public:
- /// Add a service to the manager
- void AddService(Interface* service);
-
- /// Removes a service from the manager
- void DeleteService(const std::string& port_name);
-
- /// Get a Service Interface from its Handle
- Interface* FetchFromHandle(Handle handle);
-
- /// Get a Service Interface from its port
- Interface* FetchFromPortName(const std::string& port_name);
-
-private:
- std::vector<Interface*> m_services;
- std::map<std::string, u32> m_port_map;
};
/// Initialize ServiceManager
@@ -147,8 +115,9 @@ void Init();
/// Shutdown ServiceManager
void Shutdown();
-
-extern Manager* g_manager; ///< Service manager
-
+/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC.
+extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports;
+/// Map of services registered with the "srv:" service, retrieved using GetServiceHandle.
+extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services;
} // namespace
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index f502c6afe..414c53c54 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -7,6 +7,19 @@
#if EMU_PLATFORM == PLATFORM_WINDOWS
#include <winsock2.h>
#include <ws2tcpip.h>
+
+// MinGW does not define several errno constants
+#ifndef _MSC_VER
+#define EBADMSG 104
+#define ENODATA 120
+#define ENOMSG 122
+#define ENOSR 124
+#define ENOSTR 125
+#define ETIME 137
+#define EIDRM 2001
+#define ENOLINK 2002
+#endif // _MSC_VER
+
#else
#include <sys/socket.h>
#include <netinet/in.h>
@@ -721,7 +734,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
Interface::~Interface() {
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index ac5f30a28..cc59a03ce 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -11,7 +11,7 @@
namespace SRV {
-static Handle g_event_handle = 0;
+static Kernel::SharedPtr<Kernel::Event> event_handle;
static void Initialize(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
@@ -23,11 +23,11 @@ static void GetProcSemaphore(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
// TODO(bunnei): Change to a semaphore once these have been implemented
- g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event");
- Kernel::SetEventLocked(g_event_handle, false);
+ event_handle = Kernel::Event::Create(RESETTYPE_ONESHOT, "SRV:Event");
+ event_handle->Clear();
cmd_buff[1] = 0; // No error
- cmd_buff[3] = g_event_handle;
+ cmd_buff[3] = Kernel::g_handle_table.Create(event_handle).MoveFrom();
}
static void GetServiceHandle(Service::Interface* self) {
@@ -35,10 +35,10 @@ static void GetServiceHandle(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
- Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
+ auto it = Service::g_srv_services.find(port_name);
- if (nullptr != service) {
- cmd_buff[3] = service->GetHandle();
+ if (it != Service::g_srv_services.end()) {
+ cmd_buff[3] = Kernel::g_handle_table.Create(it->second).MoveFrom();
LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
} else {
LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
@@ -63,7 +63,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp
index 360516cdf..3f49c1c97 100644
--- a/src/core/hle/service/ssl_c.cpp
+++ b/src/core/hle/service/ssl_c.cpp
@@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index b3d873ef0..fc76d2721 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -54,7 +54,7 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp
new file mode 100644
index 000000000..6033a53b4
--- /dev/null
+++ b/src/core/hle/shared_page.cpp
@@ -0,0 +1,80 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+#include "common/log.h"
+
+#include "core/core.h"
+#include "core/mem_map.h"
+#include "core/hle/config_mem.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace SharedPage {
+
+// helper macro to properly align structure members.
+// Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121",
+// depending on the current source line to make sure variable names are unique.
+#define INSERT_PADDING_BYTES_HELPER1(x, y) x ## y
+#define INSERT_PADDING_BYTES_HELPER2(x, y) INSERT_PADDING_BYTES_HELPER1(x, y)
+#define INSERT_PADDING_BYTES(num_words) u8 INSERT_PADDING_BYTES_HELPER2(pad, __LINE__)[(num_words)]
+
+// see http://3dbrew.org/wiki/Configuration_Memory#Shared_Memory_Page_For_ARM11_Processes
+
+#pragma pack(1)
+struct DateTime {
+ u64 date_time; // 0x0
+ u64 update_tick; // 0x8
+ INSERT_PADDING_BYTES(0x20 - 0x10); // 0x10
+};
+
+struct SharedPageDef {
+ // most of these names are taken from the 3dbrew page linked above.
+ u32 date_time_selector; // 0x0
+ u8 running_hw; // 0x4
+ u8 mcu_hw_info; // 0x5: don't know what the acronyms mean
+ INSERT_PADDING_BYTES(0x20 - 0x6); // 0x6
+ DateTime date_time_0; // 0x20
+ DateTime date_time_1; // 0x40
+ u8 wifi_macaddr[6]; // 0x60
+ u8 wifi_unknown1; // 0x66: 3dbrew says these are "Likely wifi hardware related"
+ u8 wifi_unknown2; // 0x67
+ INSERT_PADDING_BYTES(0x80 - 0x68); // 0x68
+ float sliderstate_3d; // 0x80
+ u8 ledstate_3d; // 0x84
+ INSERT_PADDING_BYTES(0xA0 - 0x85); // 0x85
+ u64 menu_title_id; // 0xA0
+ u64 active_menu_title_id; // 0xA8
+ INSERT_PADDING_BYTES(0x1000 - 0xB0); // 0xB0
+};
+#pragma pack()
+
+static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong");
+static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE, "Shared page structure size is wrong");
+
+static SharedPageDef shared_page;
+
+template <typename T>
+inline void Read(T &var, const u32 addr) {
+ u32 offset = addr - Memory::SHARED_PAGE_VADDR;
+ var = *(reinterpret_cast<T*>(((uintptr_t)&shared_page) + offset));
+}
+
+// Explicitly instantiate template functions because we aren't defining this in the header:
+template void Read<u64>(u64 &var, const u32 addr);
+template void Read<u32>(u32 &var, const u32 addr);
+template void Read<u16>(u16 &var, const u32 addr);
+template void Read<u8>(u8 &var, const u32 addr);
+
+void Set3DSlider(float amount) {
+ shared_page.sliderstate_3d = amount;
+ shared_page.ledstate_3d = (amount == 0.0f); // off when non-zero
+}
+
+void Init() {
+ shared_page.running_hw = 0x1; // product
+ Set3DSlider(0.0f);
+}
+
+} // namespace
diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h
new file mode 100644
index 000000000..8f93545ec
--- /dev/null
+++ b/src/core/hle/shared_page.h
@@ -0,0 +1,26 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+/**
+ * The shared page stores various runtime configuration settings. This memory page is
+ * read-only for user processes (there is a bit in the header that grants the process
+ * write access, according to 3dbrew; this is not emulated)
+ */
+
+#include "common/common_types.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace SharedPage {
+
+template <typename T>
+void Read(T &var, const u32 addr);
+
+void Set3DSlider(float amount);
+
+void Init();
+
+} // namespace
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index d3b4483ca..34a27917f 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -26,16 +26,25 @@
// Namespace SVC
using Kernel::SharedPtr;
+using Kernel::ERR_INVALID_HANDLE;
namespace SVC {
+const ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel,
+ ErrorSummary::NotFound, ErrorLevel::Permanent); // 0xD88007FA
+const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E
+
+/// An invalid result code that is meant to be overwritten when a thread resumes from waiting
+const ResultCode RESULT_INVALID(0xDEADC0DE);
+
enum ControlMemoryOperation {
MEMORY_OPERATION_HEAP = 0x00000003,
MEMORY_OPERATION_GSP_HEAP = 0x00010003,
};
/// Map application or GSP heap memory
-static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
+static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
LOG_TRACE(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
operation, addr0, addr1, size, permissions);
@@ -55,147 +64,222 @@ static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1,
default:
LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation);
}
- return 0;
+ return RESULT_SUCCESS;
}
/// Maps a memory block to specified address
-static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
+static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
+ using Kernel::SharedMemory;
+ using Kernel::MemoryPermission;
+
LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
handle, addr, permissions, other_permissions);
- Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions);
+ SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
+ if (shared_memory == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ MemoryPermission permissions_type = static_cast<MemoryPermission>(permissions);
switch (permissions_type) {
- case Kernel::MemoryPermission::Read:
- case Kernel::MemoryPermission::Write:
- case Kernel::MemoryPermission::ReadWrite:
- case Kernel::MemoryPermission::Execute:
- case Kernel::MemoryPermission::ReadExecute:
- case Kernel::MemoryPermission::WriteExecute:
- case Kernel::MemoryPermission::ReadWriteExecute:
- case Kernel::MemoryPermission::DontCare:
- Kernel::MapSharedMemory(handle, addr, permissions_type,
- static_cast<Kernel::MemoryPermission>(other_permissions));
+ case MemoryPermission::Read:
+ case MemoryPermission::Write:
+ case MemoryPermission::ReadWrite:
+ case MemoryPermission::Execute:
+ case MemoryPermission::ReadExecute:
+ case MemoryPermission::WriteExecute:
+ case MemoryPermission::ReadWriteExecute:
+ case MemoryPermission::DontCare:
+ shared_memory->Map(addr, permissions_type,
+ static_cast<MemoryPermission>(other_permissions));
break;
default:
LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
}
- return 0;
+ return RESULT_SUCCESS;
}
/// Connect to an OS service given the port name, returns the handle to the port to out
-static Result ConnectToPort(Handle* out, const char* port_name) {
- Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
+static ResultCode ConnectToPort(Handle* out_handle, const char* port_name) {
+ if (port_name == nullptr)
+ return ERR_NOT_FOUND;
+ if (std::strlen(port_name) > 11)
+ return ERR_PORT_NAME_TOO_LONG;
LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name);
- _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!");
- *out = service->GetHandle();
+ auto it = Service::g_kernel_named_ports.find(port_name);
+ if (it == Service::g_kernel_named_ports.end()) {
+ LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name);
+ return ERR_NOT_FOUND;
+ }
- return 0;
+ CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(it->second));
+ return RESULT_SUCCESS;
}
/// Synchronize to an OS service
-static Result SendSyncRequest(Handle handle) {
+static ResultCode SendSyncRequest(Handle handle) {
SharedPtr<Kernel::Session> session = Kernel::g_handle_table.Get<Kernel::Session>(handle);
if (session == nullptr) {
- return InvalidHandle(ErrorModule::Kernel).raw;
+ return ERR_INVALID_HANDLE;
}
LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str());
- ResultVal<bool> wait = session->SyncRequest();
- if (wait.Succeeded() && *wait) {
- Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
- }
-
- return wait.Code().raw;
+ return session->SyncRequest().Code();
}
/// Close a handle
-static Result CloseHandle(Handle handle) {
- // ImplementMe
- LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle);
- return 0;
+static ResultCode CloseHandle(Handle handle) {
+ LOG_TRACE(Kernel_SVC, "Closing handle 0x%08X", handle);
+ return Kernel::g_handle_table.Close(handle);
}
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
-static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
- // TODO(bunnei): Do something with nano_seconds, currently ignoring this
- bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
-
- SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handle);
+static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
+ auto object = Kernel::g_handle_table.GetWaitObject(handle);
if (object == nullptr)
- return InvalidHandle(ErrorModule::Kernel).raw;
+ return ERR_INVALID_HANDLE;
LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle,
object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds);
- ResultVal<bool> wait = object->WaitSynchronization();
-
// Check for next thread to schedule
- if (wait.Succeeded() && *wait) {
+ if (object->ShouldWait()) {
+
+ object->AddWaitingThread(Kernel::GetCurrentThread());
+ Kernel::WaitCurrentThread_WaitSynchronization(object, false, false);
+
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds);
+
HLE::Reschedule(__func__);
+
+ // NOTE: output of this SVC will be set later depending on how the thread resumes
+ return RESULT_INVALID;
}
- return wait.Code().raw;
+ object->Acquire();
+
+ return RESULT_SUCCESS;
}
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
-static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
- s64 nano_seconds) {
-
- // TODO(bunnei): Do something with nano_seconds, currently ignoring this
- bool unlock_all = true;
- bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
-
- LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld",
- handle_count, (wait_all ? "true" : "false"), nano_seconds);
-
- // Iterate through each handle, synchronize kernel object
- for (s32 i = 0; i < handle_count; i++) {
- SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[i]);
- if (object == nullptr)
- return InvalidHandle(ErrorModule::Kernel).raw;
-
- LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i],
- object->GetTypeName().c_str(), object->GetName().c_str());
-
- // TODO(yuriks): Verify how the real function behaves when an error happens here
- ResultVal<bool> wait_result = object->WaitSynchronization();
- bool wait = wait_result.Succeeded() && *wait_result;
-
- if (!wait && !wait_all) {
- *out = i;
- return RESULT_SUCCESS.raw;
- } else {
- unlock_all = false;
+static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) {
+ bool wait_thread = !wait_all;
+ int handle_index = 0;
+
+ // Check if 'handles' is invalid
+ if (handles == nullptr)
+ return ResultCode(ErrorDescription::InvalidPointer, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+
+ // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If
+ // this happens, the running application will crash.
+ _assert_msg_(Kernel, out != nullptr, "invalid output pointer specified!");
+
+ // Check if 'handle_count' is invalid
+ if (handle_count < 0)
+ return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+
+ // If 'handle_count' is non-zero, iterate through each handle and wait the current thread if
+ // necessary
+ if (handle_count != 0) {
+ bool selected = false; // True once an object has been selected
+ for (int i = 0; i < handle_count; ++i) {
+ auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
+ if (object == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ // Check if the current thread should wait on this object...
+ if (object->ShouldWait()) {
+
+ // Check we are waiting on all objects...
+ if (wait_all)
+ // Wait the thread
+ wait_thread = true;
+ } else {
+ // Do not wait on this object, check if this object should be selected...
+ if (!wait_all && !selected) {
+ // Do not wait the thread
+ wait_thread = false;
+ handle_index = i;
+ selected = true;
+ }
+ }
+ }
+ } else {
+ // If no handles were passed in, put the thread to sleep only when 'wait_all' is false
+ // NOTE: This should deadlock the current thread if no timeout was specified
+ if (!wait_all) {
+ wait_thread = true;
+ Kernel::WaitCurrentThread_WaitSynchronization(nullptr, true, wait_all);
}
}
- if (wait_all && unlock_all) {
- *out = handle_count;
- return RESULT_SUCCESS.raw;
+ // If thread should wait, then set its state to waiting and then reschedule...
+ if (wait_thread) {
+
+ // Actually wait the current thread on each object if we decided to wait...
+ for (int i = 0; i < handle_count; ++i) {
+ auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
+ object->AddWaitingThread(Kernel::GetCurrentThread());
+ Kernel::WaitCurrentThread_WaitSynchronization(object, true, wait_all);
+ }
+
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds);
+
+ HLE::Reschedule(__func__);
+
+ // NOTE: output of this SVC will be set later depending on how the thread resumes
+ return RESULT_INVALID;
}
- // Check for next thread to schedule
- HLE::Reschedule(__func__);
+ // Acquire objects if we did not wait...
+ for (int i = 0; i < handle_count; ++i) {
+ auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
+
+ // Acquire the object if it is not waiting...
+ if (!object->ShouldWait()) {
+ object->Acquire();
- return RESULT_SUCCESS.raw;
+ // If this was the first non-waiting object and 'wait_all' is false, don't acquire
+ // any other objects
+ if (!wait_all)
+ break;
+ }
+ }
+
+ // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does
+ // not seem to set it to any meaningful value.
+ *out = wait_all ? 0 : handle_index;
+
+ return RESULT_SUCCESS;
}
/// Create an address arbiter (to allocate access to shared resources)
-static Result CreateAddressArbiter(u32* arbiter) {
- Handle handle = Kernel::CreateAddressArbiter();
- *arbiter = handle;
- return 0;
+static ResultCode CreateAddressArbiter(Handle* out_handle) {
+ using Kernel::AddressArbiter;
+
+ SharedPtr<AddressArbiter> arbiter = AddressArbiter::Create();
+ CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(arbiter)));
+ LOG_TRACE(Kernel_SVC, "returned handle=0x%08X", *out_handle);
+ return RESULT_SUCCESS;
}
/// Arbitrate address
-static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
- LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", arbiter,
+static ResultCode ArbitrateAddress(Handle handle, u32 address, u32 type, u32 value, s64 nanoseconds) {
+ using Kernel::AddressArbiter;
+
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", handle,
address, type, value);
- return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type),
- address, value).raw;
+
+ SharedPtr<AddressArbiter> arbiter = Kernel::g_handle_table.Get<AddressArbiter>(handle);
+ if (arbiter == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ return arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type),
+ address, value, nanoseconds);
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
@@ -204,26 +288,26 @@ static void OutputDebugString(const char* string) {
}
/// Get resource limit
-static Result GetResourceLimit(Handle* resource_limit, Handle process) {
+static ResultCode GetResourceLimit(Handle* resource_limit, Handle process) {
// With regards to proceess values:
// 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
// the current KThread.
*resource_limit = 0xDEADBEEF;
LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called process=0x%08X", process);
- return 0;
+ return RESULT_SUCCESS;
}
/// Get resource limit current values
-static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
+static ResultCode GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
s32 name_count) {
LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d",
resource_limit, names, name_count);
Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now
- return 0;
+ return RESULT_SUCCESS;
}
/// Creates a new thread
-static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
+static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
using Kernel::Thread;
std::string name;
@@ -234,25 +318,20 @@ static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top
name = Common::StringFromFormat("unknown-%08x", entry_point);
}
- ResultVal<SharedPtr<Thread>> thread_res = Kernel::Thread::Create(
- name, entry_point, priority, arg, processor_id, stack_top, Kernel::DEFAULT_STACK_SIZE);
- if (thread_res.Failed())
- return thread_res.Code().raw;
- SharedPtr<Thread> thread = std::move(*thread_res);
-
- // TODO(yuriks): Create new handle instead of using built-in
- Core::g_app_core->SetReg(1, thread->GetHandle());
+ CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(
+ name, entry_point, priority, arg, processor_id, stack_top, Kernel::DEFAULT_STACK_SIZE));
+ CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread)));
LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
"threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
- name.c_str(), arg, stack_top, priority, processor_id, thread->GetHandle());
+ name.c_str(), arg, stack_top, priority, processor_id, *out_handle);
if (THREADPROCESSORID_1 == processor_id) {
LOG_WARNING(Kernel_SVC,
"thread designated for system CPU core (UNIMPLEMENTED) will be run with app core scheduling");
}
- return 0;
+ return RESULT_SUCCESS;
}
/// Called when a thread exits
@@ -264,127 +343,193 @@ static void ExitThread() {
}
/// Gets the priority for the specified thread
-static Result GetThreadPriority(s32* priority, Handle handle) {
+static ResultCode GetThreadPriority(s32* priority, Handle handle) {
const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
if (thread == nullptr)
- return InvalidHandle(ErrorModule::Kernel).raw;
+ return ERR_INVALID_HANDLE;
*priority = thread->GetPriority();
- return RESULT_SUCCESS.raw;
+ return RESULT_SUCCESS;
}
/// Sets the priority for the specified thread
-static Result SetThreadPriority(Handle handle, s32 priority) {
+static ResultCode SetThreadPriority(Handle handle, s32 priority) {
SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
if (thread == nullptr)
- return InvalidHandle(ErrorModule::Kernel).raw;
+ return ERR_INVALID_HANDLE;
thread->SetPriority(priority);
- return RESULT_SUCCESS.raw;
+ return RESULT_SUCCESS;
}
/// Create a mutex
-static Result CreateMutex(Handle* mutex, u32 initial_locked) {
- *mutex = Kernel::CreateMutex((initial_locked != 0));
+static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) {
+ using Kernel::Mutex;
+
+ SharedPtr<Mutex> mutex = Mutex::Create(initial_locked != 0);
+ CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(mutex)));
+
LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X",
- initial_locked ? "true" : "false", *mutex);
- return 0;
+ initial_locked ? "true" : "false", *out_handle);
+ return RESULT_SUCCESS;
}
/// Release a mutex
-static Result ReleaseMutex(Handle handle) {
+static ResultCode ReleaseMutex(Handle handle) {
+ using Kernel::Mutex;
+
LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle);
- ResultCode res = Kernel::ReleaseMutex(handle);
- return res.raw;
+
+ SharedPtr<Mutex> mutex = Kernel::g_handle_table.Get<Mutex>(handle);
+ if (mutex == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ mutex->Release();
+ return RESULT_SUCCESS;
}
/// Get the ID for the specified thread.
-static Result GetThreadId(u32* thread_id, Handle handle) {
+static ResultCode GetThreadId(u32* thread_id, Handle handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle);
const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
if (thread == nullptr)
- return InvalidHandle(ErrorModule::Kernel).raw;
+ return ERR_INVALID_HANDLE;
*thread_id = thread->GetThreadId();
- return RESULT_SUCCESS.raw;
+ return RESULT_SUCCESS;
}
/// Creates a semaphore
-static Result CreateSemaphore(Handle* semaphore, s32 initial_count, s32 max_count) {
- ResultCode res = Kernel::CreateSemaphore(semaphore, initial_count, max_count);
+static ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) {
+ using Kernel::Semaphore;
+
+ CASCADE_RESULT(SharedPtr<Semaphore> semaphore, Semaphore::Create(initial_count, max_count));
+ CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(semaphore)));
+
LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X",
- initial_count, max_count, *semaphore);
- return res.raw;
+ initial_count, max_count, *out_handle);
+ return RESULT_SUCCESS;
}
/// Releases a certain number of slots in a semaphore
-static Result ReleaseSemaphore(s32* count, Handle semaphore, s32 release_count) {
- LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, semaphore);
- ResultCode res = Kernel::ReleaseSemaphore(count, semaphore, release_count);
- return res.raw;
+static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
+ using Kernel::Semaphore;
+
+ LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, handle);
+
+ SharedPtr<Semaphore> semaphore = Kernel::g_handle_table.Get<Semaphore>(handle);
+ if (semaphore == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ CASCADE_RESULT(*count, semaphore->Release(release_count));
+ return RESULT_SUCCESS;
}
/// Query memory
-static Result QueryMemory(void* info, void* out, u32 addr) {
+static ResultCode QueryMemory(void* info, void* out, u32 addr) {
LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr);
- return 0;
+ return RESULT_SUCCESS;
}
/// Create an event
-static Result CreateEvent(Handle* evt, u32 reset_type) {
- *evt = Kernel::CreateEvent((ResetType)reset_type);
+static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) {
+ using Kernel::Event;
+
+ SharedPtr<Event> evt = Kernel::Event::Create(static_cast<ResetType>(reset_type));
+ CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(evt)));
+
LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
- reset_type, *evt);
- return 0;
+ reset_type, *out_handle);
+ return RESULT_SUCCESS;
}
/// Duplicates a kernel handle
-static Result DuplicateHandle(Handle* out, Handle handle) {
- ResultVal<Handle> out_h = Kernel::g_handle_table.Duplicate(handle);
- if (out_h.Succeeded()) {
- *out = *out_h;
- LOG_TRACE(Kernel_SVC, "duplicated 0x%08X to 0x%08X", handle, *out);
- }
- return out_h.Code().raw;
+static ResultCode DuplicateHandle(Handle* out, Handle handle) {
+ CASCADE_RESULT(*out, Kernel::g_handle_table.Duplicate(handle));
+ LOG_TRACE(Kernel_SVC, "duplicated 0x%08X to 0x%08X", handle, *out);
+ return RESULT_SUCCESS;
}
/// Signals an event
-static Result SignalEvent(Handle evt) {
- LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
- return Kernel::SignalEvent(evt).raw;
+static ResultCode SignalEvent(Handle handle) {
+ using Kernel::Event;
+ LOG_TRACE(Kernel_SVC, "called event=0x%08X", handle);
+
+ SharedPtr<Event> evt = Kernel::g_handle_table.Get<Kernel::Event>(handle);
+ if (evt == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ evt->Signal();
+ HLE::Reschedule(__func__);
+ return RESULT_SUCCESS;
}
/// Clears an event
-static Result ClearEvent(Handle evt) {
- LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
- return Kernel::ClearEvent(evt).raw;
+static ResultCode ClearEvent(Handle handle) {
+ using Kernel::Event;
+ LOG_TRACE(Kernel_SVC, "called event=0x%08X", handle);
+
+ SharedPtr<Event> evt = Kernel::g_handle_table.Get<Kernel::Event>(handle);
+ if (evt == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ evt->Clear();
+ return RESULT_SUCCESS;
}
/// Creates a timer
-static Result CreateTimer(Handle* handle, u32 reset_type) {
- ResultCode res = Kernel::CreateTimer(handle, static_cast<ResetType>(reset_type));
+static ResultCode CreateTimer(Handle* out_handle, u32 reset_type) {
+ using Kernel::Timer;
+
+ SharedPtr<Timer> timer = Timer::Create(static_cast<ResetType>(reset_type));
+ CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(timer)));
+
LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
- reset_type, *handle);
- return res.raw;
+ reset_type, *out_handle);
+ return RESULT_SUCCESS;
}
/// Clears a timer
-static Result ClearTimer(Handle handle) {
+static ResultCode ClearTimer(Handle handle) {
+ using Kernel::Timer;
+
LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
- return Kernel::ClearTimer(handle).raw;
+
+ SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
+ if (timer == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ timer->Clear();
+ return RESULT_SUCCESS;
}
/// Starts a timer
-static Result SetTimer(Handle handle, s64 initial, s64 interval) {
+static ResultCode SetTimer(Handle handle, s64 initial, s64 interval) {
+ using Kernel::Timer;
+
LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
- return Kernel::SetTimer(handle, initial, interval).raw;
+
+ SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
+ if (timer == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ timer->Set(initial, interval);
+ return RESULT_SUCCESS;
}
/// Cancels a timer
-static Result CancelTimer(Handle handle) {
+static ResultCode CancelTimer(Handle handle) {
+ using Kernel::Timer;
+
LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
- return Kernel::CancelTimer(handle).raw;
+
+ SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
+ if (timer == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ timer->Cancel();
+ return RESULT_SUCCESS;
}
/// Sleep the current thread
@@ -392,10 +537,10 @@ static void SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
// Sleep current thread and check for next thread to schedule
- Kernel::WaitCurrentThread(WAITTYPE_SLEEP);
+ Kernel::WaitCurrentThread_Sleep();
// Create an event to wake the thread up after the specified nanosecond delay has passed
- Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds);
+ Kernel::GetCurrentThread()->WakeAfterDelay(nanoseconds);
HLE::Reschedule(__func__);
}
@@ -406,15 +551,16 @@ static s64 GetSystemTick() {
}
/// Creates a memory block at the specified address with the specified permissions and size
-static Result CreateMemoryBlock(Handle* memblock, u32 addr, u32 size, u32 my_permission,
- u32 other_permission) {
-
+static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission,
+ u32 other_permission) {
+ using Kernel::SharedMemory;
// TODO(Subv): Implement this function
- Handle shared_memory = Kernel::CreateSharedMemory();
- *memblock = shared_memory;
+ SharedPtr<SharedMemory> shared_memory = SharedMemory::Create();
+ CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory)));
+
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr);
- return 0;
+ return RESULT_SUCCESS;
}
const HLE::FunctionDef SVC_Table[] = {
@@ -533,15 +679,15 @@ const HLE::FunctionDef SVC_Table[] = {
{0x70, nullptr, "ControlProcessMemory"},
{0x71, nullptr, "MapProcessMemory"},
{0x72, nullptr, "UnmapProcessMemory"},
- {0x73, nullptr, "Unknown"},
- {0x74, nullptr, "Unknown"},
- {0x75, nullptr, "Unknown"},
+ {0x73, nullptr, "CreateCodeSet"},
+ {0x74, nullptr, "RandomStub"},
+ {0x75, nullptr, "CreateProcess"},
{0x76, nullptr, "TerminateProcess"},
- {0x77, nullptr, "Unknown"},
+ {0x77, nullptr, "SetProcessResourceLimits"},
{0x78, nullptr, "CreateResourceLimit"},
- {0x79, nullptr, "Unknown"},
- {0x7A, nullptr, "Unknown"},
- {0x7B, nullptr, "Unknown"},
+ {0x79, nullptr, "SetResourceLimitValues"},
+ {0x7A, nullptr, "AddCodeSegment"},
+ {0x7B, nullptr, "Backdoor"},
{0x7C, nullptr, "KernelSetState"},
{0x7D, nullptr, "QueryProcessMemory"},
};