From ddf2f5f3c579aefea2b497b634f0dcc5e8ec8f31 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 30 Oct 2020 15:36:11 -0700 Subject: [PATCH] ddsf: implement namespace --- .../libstratosphere/include/stratosphere.hpp | 1 + .../include/stratosphere/ddsf.hpp | 10 +- .../ddsf/ddsf_device_code_entry.hpp | 110 +++++++++ .../ddsf/ddsf_device_code_entry_manager.hpp | 84 +++++++ .../ddsf/ddsf_event_handler_manager.hpp | 80 +++++++ .../stratosphere/ddsf/ddsf_i_castable.hpp | 86 +++++++ .../stratosphere/ddsf/ddsf_i_device.hpp | 140 ++++++++++++ .../stratosphere/ddsf/ddsf_i_driver.hpp | 99 ++++++++ .../ddsf/ddsf_i_event_handler.hpp | 92 ++++++++ .../stratosphere/ddsf/ddsf_i_session.hpp | 100 ++++++++ .../stratosphere/ddsf/ddsf_memory_api.hpp | 31 +++ .../include/stratosphere/ddsf/ddsf_types.hpp | 11 +- .../stratosphere/ddsf/impl/ddsf_for_each.hpp | 52 +++++ .../stratosphere/ddsf/impl/ddsf_type_tag.hpp | 70 ++++++ .../include/stratosphere/os.hpp | 1 + .../os/os_sdk_condition_variable.hpp | 74 ++++++ .../ddsf/ddsf_device_code_entry_manager.cpp | 149 ++++++++++++ .../source/ddsf/ddsf_event_handler.cpp | 215 ++++++++++++++++++ .../source/ddsf/ddsf_memory_api.cpp | 49 ++++ .../source/ddsf/ddsf_session_api.cpp | 49 ++++ .../source/os/os_sdk_condition_variable.cpp | 48 ++++ .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/ddsf_results.hpp | 31 +++ 23 files changed, 1578 insertions(+), 5 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ddsf/ddsf_event_handler_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_castable.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_device.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_driver.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_event_handler.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_session.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ddsf/ddsf_memory_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_for_each.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_type_tag.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_sdk_condition_variable.hpp create mode 100644 libraries/libstratosphere/source/ddsf/ddsf_device_code_entry_manager.cpp create mode 100644 libraries/libstratosphere/source/ddsf/ddsf_event_handler.cpp create mode 100644 libraries/libstratosphere/source/ddsf/ddsf_memory_api.cpp create mode 100644 libraries/libstratosphere/source/ddsf/ddsf_session_api.cpp create mode 100644 libraries/libstratosphere/source/os/os_sdk_condition_variable.cpp create mode 100644 libraries/libvapours/include/vapours/results/ddsf_results.hpp diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index 6096ca240..a2af48d3a 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/ddsf.hpp b/libraries/libstratosphere/include/stratosphere/ddsf.hpp index 000a4aedd..9d1bb7fc7 100644 --- a/libraries/libstratosphere/include/stratosphere/ddsf.hpp +++ b/libraries/libstratosphere/include/stratosphere/ddsf.hpp @@ -13,7 +13,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once #include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp new file mode 100644 index 000000000..376601bbb --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ddsf { + + class IDevice; + + class DeviceCodeEntry { + NON_COPYABLE(DeviceCodeEntry); + NON_MOVEABLE(DeviceCodeEntry); + private: + ams::DeviceCode device_code = ams::InvalidDeviceCode; + IDevice *device = nullptr; + public: + constexpr DeviceCodeEntry(ams::DeviceCode dc, IDevice *dev) : device_code(dc), device(dev) { + AMS_ASSERT(dev != nullptr); + } + + constexpr ams::DeviceCode GetDeviceCode() const { + return this->device_code; + } + + constexpr IDevice &GetDevice() { + return *this->device; + } + + constexpr const IDevice &GetDevice() const { + return *this->device; + } + }; + + class DeviceCodeEntryHolder { + NON_COPYABLE(DeviceCodeEntryHolder); + NON_MOVEABLE(DeviceCodeEntryHolder); + private: + util::IntrusiveListNode list_node; + TYPED_STORAGE(DeviceCodeEntry) entry_storage; + bool is_constructed; + public: + using ListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&DeviceCodeEntryHolder::list_node>; + using List = typename ListTraits::ListType; + friend class util::IntrusiveList>; + public: + DeviceCodeEntryHolder() : list_node(), entry_storage(), is_constructed(false) { + /* ... */ + } + + ~DeviceCodeEntryHolder() { + if (this->IsConstructed()) { + this->Destroy(); + } + } + + void AddTo(List &list) { + list.push_back(*this); + } + + void RemoveFrom(List list) { + list.erase(list.iterator_to(*this)); + } + + bool IsLinkedToList() const { + return this->list_node.IsLinked(); + } + + DeviceCodeEntry &Construct(DeviceCode dc, IDevice *dev) { + AMS_ASSERT(!this->IsConstructed()); + DeviceCodeEntry *entry = new (GetPointer(this->entry_storage)) DeviceCodeEntry(dc, dev); + this->is_constructed = true; + return *entry; + } + + bool IsConstructed() const { + return this->is_constructed; + } + + void Destroy() { + AMS_ASSERT(this->IsConstructed()); + GetReference(this->entry_storage).~DeviceCodeEntry(); + this->is_constructed = false; + } + + DeviceCodeEntry &Get() { + AMS_ASSERT(this->IsConstructed()); + return GetReference(this->entry_storage); + } + + const DeviceCodeEntry &Get() const { + AMS_ASSERT(this->IsConstructed()); + return GetReference(this->entry_storage); + } + }; + static_assert(DeviceCodeEntryHolder::ListTraits::IsValid()); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry_manager.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry_manager.hpp new file mode 100644 index 000000000..c5b747af5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry_manager.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::ddsf { + + class IDevice; + + class DeviceCodeEntryManager { + private: + ams::MemoryResource *memory_resource; + ddsf::DeviceCodeEntryHolder::List entry_list; + mutable os::SdkMutex entry_list_lock; + private: + void DestroyAllEntries() { + auto it = this->entry_list.begin(); + while (it != this->entry_list.end()) { + ddsf::DeviceCodeEntryHolder *entry = std::addressof(*it); + it = this->entry_list.erase(it); + + AMS_ASSERT(entry->IsConstructed()); + if (entry->IsConstructed()) { + entry->Destroy(); + } + + this->memory_resource->Deallocate(entry, sizeof(*entry)); + } + } + public: + DeviceCodeEntryManager(ams::MemoryResource *mr) : memory_resource(mr), entry_list(), entry_list_lock() { /* ... */ } + + ~DeviceCodeEntryManager() { + this->DestroyAllEntries(); + } + + void Reset() { + std::scoped_lock lk(this->entry_list_lock); + this->DestroyAllEntries(); + } + + Result Add(DeviceCode device_code, IDevice *device); + bool Remove(DeviceCode device_code); + + Result FindDeviceCodeEntry(DeviceCodeEntry **out, DeviceCode device_code); + Result FindDeviceCodeEntry(const DeviceCodeEntry **out, DeviceCode device_code) const; + + Result FindDevice(IDevice **out, DeviceCode device_code); + Result FindDevice(const IDevice **out, DeviceCode device_code) const; + + template + int ForEachEntry(F f) { + return impl::ForEach(this->entry_list_lock, this->entry_list, [&](DeviceCodeEntryHolder &holder) -> bool { + AMS_ASSERT(holder.IsConstructed()); + return f(holder.Get()); + }); + } + + template + int ForEachEntry(F f) const { + return impl::ForEach(this->entry_list_lock, this->entry_list, [&](const DeviceCodeEntryHolder &holder) -> bool { + AMS_ASSERT(holder.IsConstructed()); + return f(holder.Get()); + }); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_event_handler_manager.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_event_handler_manager.hpp new file mode 100644 index 000000000..1b1dca9b1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_event_handler_manager.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::ddsf { + + class EventHandlerManager; + + class EventHandlerManager { + NON_COPYABLE(EventHandlerManager); + NON_MOVEABLE(EventHandlerManager); + private: + struct LoopControlCommandParameters; + private: + bool is_initialized; + bool is_looping; + os::SdkConditionVariable is_looping_cv; + os::WaitableManagerType waitable_manager; + os::ThreadType *loop_thread; + os::Event loop_control_event; + os::WaitableHolderType loop_control_event_holder; + LoopControlCommandParameters *loop_control_command_params; + os::Event loop_control_command_done_event; /* TODO: os::LightEvent, requires mesosphere for < 4.0.0 compat. */ + os::SdkMutex loop_control_lock; + private: + void ProcessControlCommand(LoopControlCommandParameters *params); + void ProcessControlCommandImpl(LoopControlCommandParameters *params); + public: + EventHandlerManager() + : is_initialized(false), is_looping(false), is_looping_cv(), waitable_manager(), + loop_thread(), loop_control_event(os::EventClearMode_AutoClear), loop_control_event_holder(), + loop_control_command_params(), loop_control_command_done_event(os::EventClearMode_AutoClear), + loop_control_lock() + { + /* ... */ + } + + ~EventHandlerManager() { + if (this->is_looping) { + AMS_ASSERT(!this->IsRunningOnLoopThread()); + this->RequestStop(); + } + if (this->is_initialized) { + this->Finalize(); + } + } + + bool IsRunningOnLoopThread() const { return this->loop_thread == os::GetCurrentThread(); } + bool IsLooping() const { return this->is_looping; } + + void Initialize(); + void Finalize(); + + void RegisterHandler(IEventHandler *handler); + void UnregisterHandler(IEventHandler *handler); + + void WaitLoopEnter(); + void WaitLoopExit(); + void RequestStop(); + + void LoopAuto(); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_castable.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_castable.hpp new file mode 100644 index 000000000..6707f5230 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_castable.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::ddsf { + + #if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING) + + #define AMS_DDSF_CASTABLE_TRAITS(__CLASS__, __BASE__) \ + public: \ + static constexpr inline ::ams::ddsf::impl::TypeTag s_ams_ddsf_castable_type_tag(#__CLASS__, __BASE__::s_ams_ddsf_castable_type_tag); \ + constexpr virtual const ::ams::ddsf::impl::TypeTag &GetTypeTag() const override { return s_ams_ddsf_castable_type_tag; } + + #else + + #define AMS_DDSF_CASTABLE_TRAITS(__CLASS__, __BASE__) \ + public: \ + static constexpr inline ::ams::ddsf::impl::TypeTag s_ams_ddsf_castable_type_tag(__BASE__::s_ams_ddsf_castable_type_tag); \ + constexpr virtual const ::ams::ddsf::impl::TypeTag &GetTypeTag() const override { return s_ams_ddsf_castable_type_tag; } + + #endif + + class ICastable { + private: + constexpr virtual const impl::TypeTag &GetTypeTag() const = 0; + + template + constexpr ALWAYS_INLINE void AssertCastableTo() const { + AMS_ASSERT(this->IsCastableTo()); + } + public: + template + constexpr bool IsCastableTo() const { + return this->GetTypeTag().DerivesFrom(T::s_ams_ddsf_castable_type_tag); + } + + template + constexpr T &SafeCastTo() { + this->AssertCastableTo(); + return static_cast(*this); + } + + template + constexpr const T &SafeCastTo() const { + this->AssertCastableTo(); + return static_cast(*this); + } + + template + constexpr T *SafeCastToPointer() { + this->AssertCastableTo(); + return static_cast(this); + } + + template + constexpr const T *SafeCastToPointer() const { + this->AssertCastableTo(); + return static_cast(this); + } + + #if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) + + constexpr const char *GetClassName() const { + return this->GetTypeTag().GetClassName(); + } + + #endif + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_device.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_device.hpp new file mode 100644 index 000000000..78c1a8e4c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_device.hpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace ams::ddsf { + + class IDriver; + + class IDevice : public ICastable { + friend Result OpenSession(IDevice *device, ISession *session, AccessMode mode); + friend void CloseSession(ISession *session); + friend class IDriver; + public: + AMS_DDSF_CASTABLE_ROOT_TRAITS(ams::ddsf::IDevice); + private: + util::IntrusiveListNode list_node; + IDriver *driver; + ISession::List session_list; + mutable os::SdkMutex session_list_lock; + bool is_exclusive_write; + public: + using ListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&IDevice::list_node>; + using List = typename ListTraits::ListType; + friend class util::IntrusiveList>; + private: + Result AttachSession(ISession *session) { + AMS_ASSERT(session != nullptr); + std::scoped_lock lk(this->session_list_lock); + + /* Check if we're allowed to attach the session. */ + if (this->is_exclusive_write && session->CheckExclusiveWrite()) { + for (const auto &attached : this->session_list) { + R_UNLESS(!attached.CheckAccess(AccessMode_Write), ddsf::ResultAccessModeDenied()); + } + } + + /* Attach the session. */ + this->session_list.push_back(*session); + return ResultSuccess(); + } + + void DetachSession(ISession *session) { + AMS_ASSERT(session != nullptr); + std::scoped_lock lk(this->session_list_lock); + this->session_list.erase(this->session_list.iterator_to(*session)); + } + + void AttachDriver(IDriver *drv) { + AMS_ASSERT(drv != nullptr); + AMS_ASSERT(!this->IsDriverAttached()); + this->driver = drv; + AMS_ASSERT(this->IsDriverAttached()); + } + + void DetachDriver() { + AMS_ASSERT(this->IsDriverAttached()); + this->driver = nullptr; + AMS_ASSERT(!this->IsDriverAttached()); + } + public: + IDevice(bool exclusive_write) : list_node(), driver(nullptr), session_list(), session_list_lock(), is_exclusive_write(exclusive_write) { + this->session_list.clear(); + } + protected: + ~IDevice() { + this->session_list.clear(); + } + public: + void AddTo(List &list) { + list.push_back(*this); + } + + void RemoveFrom(List list) { + list.erase(list.iterator_to(*this)); + } + + bool IsLinkedToList() const { + return this->list_node.IsLinked(); + } + + IDriver &GetDriver() { + AMS_ASSERT(this->IsDriverAttached()); + return *this->driver; + } + + const IDriver &GetDriver() const { + AMS_ASSERT(this->IsDriverAttached()); + return *this->driver; + } + + bool IsDriverAttached() const { + return this->driver != nullptr; + } + + template + Result ForEachSession(F f, bool return_on_fail) { + return impl::ForEach(this->session_list_lock, this->session_list, f, return_on_fail); + } + + template + Result ForEachSession(F f, bool return_on_fail) const { + return impl::ForEach(this->session_list_lock, this->session_list, f, return_on_fail); + } + + template + int ForEachSession(F f) { + return impl::ForEach(this->session_list_lock, this->session_list, f); + } + + template + int ForEachSession(F f) const { + return impl::ForEach(this->session_list_lock, this->session_list, f); + } + + bool HasAnyOpenSession() const { + return !this->session_list.empty(); + } + }; + static_assert(IDevice::ListTraits::IsValid()); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_driver.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_driver.hpp new file mode 100644 index 000000000..6b4838ae6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_driver.hpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace ams::ddsf { + + class IDriver : public ICastable { + public: + AMS_DDSF_CASTABLE_ROOT_TRAITS(ams::ddsf::IDriver); + private: + util::IntrusiveListNode list_node; + IDevice::List device_list; + mutable os::SdkMutex device_list_lock; + public: + using ListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&IDriver::list_node>; + using List = typename ListTraits::ListType; + friend class util::IntrusiveList>; + private: + public: + IDriver() : list_node(), device_list(), device_list_lock() { + this->device_list.clear(); + } + protected: + ~IDriver() { + this->device_list.clear(); + } + public: + void AddTo(List &list) { + list.push_back(*this); + } + + void RemoveFrom(List list) { + list.erase(list.iterator_to(*this)); + } + + bool IsLinkedToList() const { + return this->list_node.IsLinked(); + } + + bool HasAnyDevice() const { + return !this->device_list.empty(); + } + + void RegisterDevice(IDevice *dev) { + AMS_ASSERT(dev != nullptr); + std::scoped_lock lk(this->device_list_lock); + dev->AttachDriver(this); + this->device_list.push_back(*dev); + } + + void UnregisterDevice(IDevice *dev) { + AMS_ASSERT(dev != nullptr); + std::scoped_lock lk(this->device_list_lock); + this->device_list.erase(this->device_list.iterator_to(*dev)); + dev->DetachDriver(); + } + + template + Result ForEachDevice(F f, bool return_on_fail) { + return impl::ForEach(this->device_list_lock, this->device_list, f, return_on_fail); + } + + template + Result ForEachDevice(F f, bool return_on_fail) const { + return impl::ForEach(this->device_list_lock, this->device_list, f, return_on_fail); + } + + template + int ForEachDevice(F f) { + return impl::ForEach(this->device_list_lock, this->device_list, f); + } + + template + int ForEachDevice(F f) const { + return impl::ForEach(this->device_list_lock, this->device_list, f); + } + }; + static_assert(IDriver::ListTraits::IsValid()); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_event_handler.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_event_handler.hpp new file mode 100644 index 000000000..4d85f1425 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_event_handler.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::ddsf { + + class EventHandlerManager; + + class IEventHandler { + NON_COPYABLE(IEventHandler); + NON_MOVEABLE(IEventHandler); + friend class EventHandlerManager; + private: + os::WaitableHolderType holder; + uintptr_t user_data; + bool is_initialized; + bool is_registered; + private: + void Link(os::WaitableManagerType *manager) { + AMS_ASSERT(this->IsInitialized()); + AMS_ASSERT(!this->IsRegistered()); + AMS_ASSERT(manager != nullptr); + os::LinkWaitableHolder(manager, std::addressof(this->holder)); + } + + void Unlink() { + AMS_ASSERT(this->IsInitialized()); + AMS_ASSERT(this->IsRegistered()); + os::UnlinkWaitableHolder(std::addressof(this->holder)); + } + + static IEventHandler &ToEventHandler(os::WaitableHolderType *holder) { + AMS_ASSERT(holder != nullptr); + auto &event_handler = *reinterpret_cast(os::GetWaitableHolderUserData(holder)); + AMS_ASSERT(event_handler.IsInitialized()); + return event_handler; + } + public: + IEventHandler() : holder(), user_data(0), is_initialized(false), is_registered(false) { /* ... */ } + + ~IEventHandler() { + if (this->IsRegistered()) { + this->Unlink(); + } + if (this->IsInitialized()) { + this->Finalize(); + } + } + + bool IsInitialized() const { return this->is_initialized; } + bool IsRegistered() const { return this->is_registered; } + + uintptr_t GetUserData() const { return this->user_data; } + void SetUserData(uintptr_t d) { this->user_data = d; } + + template + void Initialize(T *object) { + AMS_ASSERT(object != nullptr); + AMS_ASSERT(!this->IsInitialized()); + os::InitializeWaitableHolder(std::addressof(this->holder), object); + os::SetWaitableHolderUserData(std::addressof(this->holder), reinterpret_cast(this)); + this->is_initialized = true; + this->is_registered = false; + } + + void Finalize() { + AMS_ASSERT(this->IsInitialized()); + AMS_ASSERT(!this->IsRegistered()); + os::FinalizeWaitableHolder(std::addressof(this->holder)); + this->is_initialized = false; + this->is_registered = false; + } + protected: + virtual void HandleEvent() = 0; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_session.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_session.hpp new file mode 100644 index 000000000..4cc8b0610 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_session.hpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::ddsf { + + class ISession; + class IDevice; + + Result OpenSession(IDevice *device, ISession *session, AccessMode access_mode); + void CloseSession(ISession *session); + + class ISession : public ICastable { + friend Result OpenSession(IDevice *device, ISession *session, AccessMode mode); + friend void CloseSession(ISession *session); + public: + AMS_DDSF_CASTABLE_ROOT_TRAITS(ams::ddsf::IDevice); + private: + util::IntrusiveListNode list_node; + IDevice *device; + AccessMode access_mode; + public: + using ListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&ISession::list_node>; + using List = typename ListTraits::ListType; + friend class util::IntrusiveList>; + private: + void AttachDevice(IDevice *dev, AccessMode mode) { + AMS_ASSERT(dev != nullptr); + AMS_ASSERT(!this->IsOpen()); + this->device = dev; + this->access_mode = mode; + AMS_ASSERT(this->IsOpen()); + } + + void DetachDevice() { + AMS_ASSERT(this->IsOpen()); + this->device = nullptr; + this->access_mode = AccessMode_None; + AMS_ASSERT(!this->IsOpen()); + } + public: + ISession() : list_node(), device(nullptr), access_mode() { /* ... */ } + protected: + ~ISession() { this->DetachDevice(); AMS_ASSERT(!this->IsOpen()); } + public: + void AddTo(List &list) { + list.push_back(*this); + } + + void RemoveFrom(List list) { + list.erase(list.iterator_to(*this)); + } + + bool IsLinkedToList() const { + return this->list_node.IsLinked(); + } + + IDevice &GetDevice() { + AMS_ASSERT(this->IsOpen()); + return *this->device; + } + + const IDevice &GetDevice() const { + AMS_ASSERT(this->IsOpen()); + return *this->device; + } + + bool IsOpen() const { + return this->device != nullptr; + } + + bool CheckAccess(AccessMode mode) const { + AMS_ASSERT(this->IsOpen()); + return ((~this->access_mode) & mode) == 0; + } + + bool CheckExclusiveWrite() const { + return this->CheckAccess(AccessMode_Write) && !this->CheckAccess(AccessMode_Shared); + } + }; + static_assert(ISession::ListTraits::IsValid()); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_memory_api.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_memory_api.hpp new file mode 100644 index 000000000..094523ba3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_memory_api.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::ddsf { + + void SetMemoryResource(ams::MemoryResource *mr); + ams::MemoryResource *GetMemoryResource(); + + static constexpr size_t DeviceCodeEntryHolderSize = sizeof(DeviceCodeEntryHolder); + + void SetDeviceCodeEntryHolderMemoryResource(ams::MemoryResource *mr); + ams::MemoryResource *GetDeviceCodeEntryHolderMemoryResource(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_types.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_types.hpp index 467c48dbe..c46f3ca8d 100644 --- a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_types.hpp @@ -20,11 +20,14 @@ namespace ams::ddsf { enum AccessMode { - AccessMode_None = (0u << 0), - AccessMode_Read = (1u << 0), - AccessMode_Write = (1u << 1), + AccessMode_None = (0u << 0), + AccessMode_Read = (1u << 0), + AccessMode_Write = (1u << 1), + AccessMode_Shared = (1u << 2), - AccessMode_ReadWrite = AccessMode_Read | AccessMode_Write, + AccessMode_ReadWrite = AccessMode_Read | AccessMode_Write, + AccessMode_WriteShared = AccessMode_Write | AccessMode_Shared, + AccessMode_ReadWriteShared = AccessMode_Read | AccessMode_WriteShared, }; } diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_for_each.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_for_each.hpp new file mode 100644 index 000000000..5e62d50ff --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_for_each.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::ddsf::impl { + + template + inline Result ForEach(Lock &lock, List &list, F f, bool return_on_fail) { + std::scoped_lock lk(lock); + + Result result = ResultSuccess(); + for (auto && it : list) { + if (const auto cur_result = f(std::addressof(it)); R_FAILED(cur_result)) { + if (return_on_fail) { + return cur_result; + } else if (R_SUCCEEDED(result)) { + result = cur_result; + } + } + } + return result; + } + + template + inline int ForEach(Lock &lock, List &list, F f) { + std::scoped_lock lk(lock); + + int success_count = 0; + for (auto && it : list) { + if (!f(it)) { + return success_count; + } + ++success_count; + } + return success_count; + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_type_tag.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_type_tag.hpp new file mode 100644 index 000000000..a5b5371db --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_type_tag.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::ddsf::impl { + + #if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING) + + #define AMS_DDSF_CASTABLE_ROOT_TRAITS(__CLASS__) \ + public: \ + static constexpr inline ::ams::ddsf::impl::TypeTag s_ams_ddsf_castable_type_tag(#__CLASS__); \ + constexpr virtual const ::ams::ddsf::impl::TypeTag &GetTypeTag() const override { return s_ams_ddsf_castable_type_tag; } + + #else + + #define AMS_DDSF_CASTABLE_ROOT_TRAITS(__CLASS__) \ + public: \ + static constexpr inline ::ams::ddsf::impl::TypeTag s_ams_ddsf_castable_type_tag{}; \ + constexpr virtual const ::ams::ddsf::impl::TypeTag &GetTypeTag() const override { return s_ams_ddsf_castable_type_tag; } + + #endif + + class TypeTag { + private: + const char * const class_name; + const TypeTag * const base; + public: + #if !(defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING)) + constexpr TypeTag() : class_name(nullptr), base(nullptr) { /* ... */} + constexpr TypeTag(const TypeTag &b) : class_name(nullptr), base(std::addressof(b)) { AMS_ASSERT(this != this->base); } + + constexpr TypeTag(const char *c) : class_name(nullptr), base(nullptr) { AMS_UNUSED(c); } + constexpr TypeTag(const char *c, const TypeTag &b) : class_name(nullptr), base(std::addressof(b)) { AMS_UNUSED(c); AMS_ASSERT(this != this->base); } + #else + constexpr TypeTag(const char *c) : class_name(c), base(nullptr) { /* ... */ } + constexpr TypeTag(const char *c, const TypeTag &b) : class_name(c), base(std::addressof(b)) { AMS_ASSERT(this != this->base); } + #endif + + constexpr const char * GetClassName() const { return this->class_name; } + + constexpr bool Is(const TypeTag &rhs) const { return this == std::addressof(rhs); } + + constexpr bool DerivesFrom(const TypeTag &rhs) const { + const TypeTag * cur = this; + while (cur != nullptr) { + if (cur == std::addressof(rhs)) { + return true; + } + cur = cur->base; + } + return false; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index ea37812ac..59d5e34e1 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_sdk_condition_variable.hpp b/libraries/libstratosphere/include/stratosphere/os/os_sdk_condition_variable.hpp new file mode 100644 index 000000000..3d5e38b8a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_sdk_condition_variable.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::os { + + struct SdkConditionVariableType { + union { + s32 _arr[sizeof(impl::InternalConditionVariableStorage) / sizeof(s32)]; + impl::InternalConditionVariableStorage _storage; + }; + + void Initialize() { + GetReference(this->_storage).Initialize(); + } + + void Wait(SdkMutexType &mutex); + bool TimedWait(SdkMutexType &mutex, TimeSpan timeout); + + /* TODO: SdkRecursiveMutexType */ + + void Signal() { + GetReference(this->_storage).Signal(); + } + + void Broadcast() { + GetReference(this->_storage).Broadcast(); + } + }; + static_assert(std::is_trivial::value); + + class SdkConditionVariable { + private: + SdkConditionVariableType cv; + public: + constexpr SdkConditionVariable() : cv{{0}} { /* ... */ } + + void Wait(SdkMutex &m) { + return this->cv.Wait(m.mutex); + } + + bool TimedWait(SdkMutex &m, TimeSpan timeout) { + return this->cv.TimedWait(m.mutex, timeout); + } + + /* TODO: SdkRecursiveMutexType */ + + void Signal() { + return this->cv.Signal(); + } + + void Broadcast() { + return this->cv.Broadcast(); + } + }; + +} diff --git a/libraries/libstratosphere/source/ddsf/ddsf_device_code_entry_manager.cpp b/libraries/libstratosphere/source/ddsf/ddsf_device_code_entry_manager.cpp new file mode 100644 index 000000000..93f37ea9d --- /dev/null +++ b/libraries/libstratosphere/source/ddsf/ddsf_device_code_entry_manager.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ddsf { + + Result DeviceCodeEntryManager::Add(DeviceCode device_code, IDevice *device) { + /* Check pre-conditions. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(device->IsDriverAttached()); + + /* Acquire exclusive access to the manager. */ + std::scoped_lock lk(this->entry_list_lock); + + /* Check that we don't already have an entry with the code. */ + for (const auto &holder : this->entry_list) { + AMS_ASSERT(holder.IsConstructed()); + AMS_ASSERT(holder.Get().GetDeviceCode() != device_code); + } + + /* Allocate memory for a new device code entry holder. */ + void *holder_storage = this->memory_resource->Allocate(sizeof(DeviceCodeEntryHolder)); + R_UNLESS(holder_storage != nullptr, ddsf::ResultOutOfResource()); + + /* Initialize the new holder. */ + auto *holder = new (static_cast(holder_storage)) DeviceCodeEntryHolder; + holder->Construct(device_code, device); + + /* Link the new holder. */ + holder->AddTo(this->entry_list); + + return ResultSuccess(); + } + + bool DeviceCodeEntryManager::Remove(DeviceCode device_code) { + /* Acquire exclusive access to the manager. */ + std::scoped_lock lk(this->entry_list_lock); + + /* Find and erase the entry. */ + bool erased = false; + for (auto it = this->entry_list.begin(); it != this->entry_list.end(); /* ... */) { + /* Get the current entry, and advance the iterator. */ + DeviceCodeEntryHolder *cur = std::addressof(*(it++)); + + /* If the entry matches the device code, remove it. */ + AMS_ASSERT(cur->IsConstructed()); + if (cur->Get().GetDeviceCode() == device_code) { + /* Destroy and deallocate the holder. */ + cur->Destroy(); + cur->~DeviceCodeEntryHolder(); + this->memory_resource->Deallocate(cur, sizeof(*cur)); + + erased = true; + } + } + + return erased; + } + + Result DeviceCodeEntryManager::FindDeviceCodeEntry(DeviceCodeEntry **out, DeviceCode device_code) { + /* Check arguments. */ + AMS_ASSERT(out != nullptr); + R_UNLESS(out != nullptr, ddsf::ResultInvalidArgument()); + + /* Find the device. */ + bool found = false; + this->ForEachEntry([&](DeviceCodeEntry &entry) -> bool { + if (entry.GetDeviceCode() == device_code) { + found = true; + *out = std::addressof(entry); + return false; + } + return true; + }); + + /* Check that we found the device. */ + R_UNLESS(found, ddsf::ResultDeviceCodeNotFound()); + + return ResultSuccess(); + } + + Result DeviceCodeEntryManager::FindDeviceCodeEntry(const DeviceCodeEntry **out, DeviceCode device_code) const { + /* Check arguments. */ + AMS_ASSERT(out != nullptr); + R_UNLESS(out != nullptr, ddsf::ResultInvalidArgument()); + + /* Find the device. */ + bool found = false; + this->ForEachEntry([&](const DeviceCodeEntry &entry) -> bool { + if (entry.GetDeviceCode() == device_code) { + found = true; + *out = std::addressof(entry); + return false; + } + return true; + }); + + /* Check that we found the device. */ + R_UNLESS(found, ddsf::ResultDeviceCodeNotFound()); + + return ResultSuccess(); + } + + Result DeviceCodeEntryManager::FindDevice(IDevice **out, DeviceCode device_code) { + /* Check pre-conditions. */ + AMS_ASSERT(out != nullptr); + + /* Find the entry. */ + DeviceCodeEntry *entry; + R_TRY(this->FindDeviceCodeEntry(std::addressof(entry), device_code)); + + /* Set the output. */ + if (out != nullptr) { + *out = std::addressof(entry->GetDevice()); + } + + return ResultSuccess(); + } + + Result DeviceCodeEntryManager::FindDevice(const IDevice **out, DeviceCode device_code) const { + /* Check pre-conditions. */ + AMS_ASSERT(out != nullptr); + + /* Find the entry. */ + const DeviceCodeEntry *entry; + R_TRY(this->FindDeviceCodeEntry(std::addressof(entry), device_code)); + + /* Set the output. */ + if (out != nullptr) { + *out = std::addressof(entry->GetDevice()); + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/ddsf/ddsf_event_handler.cpp b/libraries/libstratosphere/source/ddsf/ddsf_event_handler.cpp new file mode 100644 index 000000000..7672011db --- /dev/null +++ b/libraries/libstratosphere/source/ddsf/ddsf_event_handler.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ddsf { + + namespace { + + enum class LoopControlCommand { + None = 0, + Register = 1, + Unregister = 2, + Terminate = 3, + }; + + } + + struct EventHandlerManager::LoopControlCommandParameters { + LoopControlCommand command; + IEventHandler *target; + + LoopControlCommandParameters() : command(LoopControlCommand::None), target(nullptr) { /* ... */ } + LoopControlCommandParameters(LoopControlCommand c, IEventHandler *t) : command(c), target(t) { /* ... */ } + }; + + void EventHandlerManager::Initialize() { + /* Check that we're not already initialized. */ + AMS_ASSERT(!this->is_initialized); + if (this->is_initialized) { + return; + } + + /* Initialize waitable manager/holder. */ + os::InitializeWaitableManager(std::addressof(this->waitable_manager)); + os::InitializeWaitableHolder(std::addressof(this->loop_control_event_holder), this->loop_control_event.GetBase()); + os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->loop_control_event_holder)); + + this->is_initialized = true; + } + + void EventHandlerManager::Finalize() { + /* Check that we're initialized and not looping. */ + AMS_ASSERT(!this->is_looping); + AMS_ASSERT(this->is_initialized); + if (!this->is_initialized) { + return; + } + + /* Finalize waitable manager/holder. */ + os::UnlinkWaitableHolder(std::addressof(this->loop_control_event_holder)); + os::FinalizeWaitableHolder(std::addressof(this->loop_control_event_holder)); + os::FinalizeWaitableManager(std::addressof(this->waitable_manager)); + + this->is_initialized = false; + } + + void EventHandlerManager::ProcessControlCommand(LoopControlCommandParameters *params) { + /* Check pre-conditions. */ + AMS_ASSERT(this->is_initialized); + AMS_ASSERT(params != nullptr); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->loop_control_lock); + + /* If we're processing for the loop thread, we can directly handle. */ + if (!this->is_looping || this->IsRunningOnLoopThread()) { + this->ProcessControlCommandImpl(params); + } else { + /* Otherwise, signal to the loop thread. */ + this->loop_control_command_params = params; + this->loop_control_event.Signal(); + this->loop_control_command_done_event.Wait(); + } + } + + void EventHandlerManager::ProcessControlCommandImpl(LoopControlCommandParameters *params) { + /* Check pre-conditions. */ + AMS_ASSERT(this->loop_control_lock.IsLockedByCurrentThread() || !this->loop_control_lock.TryLock()); + AMS_ASSERT(params != nullptr); + AMS_ASSERT(params->target != nullptr); + + /* Process the command. */ + switch (params->command) { + case LoopControlCommand::Register: + params->target->Link(std::addressof(this->waitable_manager)); + break; + case LoopControlCommand::Unregister: + params->target->Unlink(); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void EventHandlerManager::RegisterHandler(IEventHandler *handler) { + /* Check that the handler is valid. */ + AMS_ASSERT(handler != nullptr); + AMS_ASSERT(handler->IsInitialized()); + + /* Send registration command. */ + LoopControlCommandParameters params(LoopControlCommand::Register, handler); + return this->ProcessControlCommand(std::addressof(params)); + } + + void EventHandlerManager::UnregisterHandler(IEventHandler *handler) { + /* Check that the handler is valid. */ + AMS_ASSERT(handler != nullptr); + AMS_ASSERT(handler->IsInitialized()); + + /* Send registration command. */ + LoopControlCommandParameters params(LoopControlCommand::Unregister, handler); + return this->ProcessControlCommand(std::addressof(params)); + } + + void EventHandlerManager::WaitLoopEnter() { + /* Acquire exclusive access. */ + std::scoped_lock lk(this->loop_control_lock); + + /* Wait until we're looping. */ + while (!this->is_looping) { + this->is_looping_cv.Wait(this->loop_control_lock); + } + } + + void EventHandlerManager::WaitLoopExit() { + /* Acquire exclusive access. */ + std::scoped_lock lk(this->loop_control_lock); + + /* Wait until we're not looping. */ + while (this->is_looping) { + this->is_looping_cv.Wait(this->loop_control_lock); + } + } + + void EventHandlerManager::RequestStop() { + /* Check that we're looping and not the loop thread. */ + AMS_ASSERT(this->is_looping); + AMS_ASSERT(!this->IsRunningOnLoopThread()); + + if (this->is_looping) { + /* Acquire exclusive access. */ + std::scoped_lock lk(this->loop_control_lock); + + /* Signal to the loop thread. */ + LoopControlCommandParameters params(LoopControlCommand::Terminate, nullptr); + this->loop_control_command_params = std::addressof(params); + this->loop_control_event.Signal(); + this->loop_control_command_done_event.Wait(); + } + } + + void EventHandlerManager::LoopAuto() { + /* Check that we're not already looping. */ + AMS_ASSERT(!this->is_looping); + + /* Begin looping with the current thread. */ + this->loop_thread = os::GetCurrentThread(); + this->is_looping = true; + this->is_looping_cv.Broadcast(); + + /* Whenever we're done looping, clean up. */ + ON_SCOPE_EXIT { + this->loop_thread = nullptr; + this->is_looping = false; + this->is_looping_cv.Broadcast(); + }; + + /* Loop until we're asked to stop. */ + bool should_terminate = false; + while (!should_terminate) { + /* Wait for a holder to be signaled. */ + os::WaitableHolderType *event_holder = os::WaitAny(std::addressof(this->waitable_manager)); + AMS_ASSERT(event_holder != nullptr); + + /* Check if we have a request to handle. */ + if (event_holder == std::addressof(this->loop_control_event_holder)) { + /* Check that the request hasn't already been handled. */ + if (this->loop_control_event.TryWait()) { + /* Handle the request. */ + AMS_ASSERT(this->loop_control_command_params != nullptr); + switch (this->loop_control_command_params->command) { + case LoopControlCommand::Register: + case LoopControlCommand::Unregister: + this->ProcessControlCommandImpl(this->loop_control_command_params); + break; + case LoopControlCommand::Terminate: + should_terminate = true; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Clear the request, and signal that it's done. */ + this->loop_control_command_params = nullptr; + this->loop_control_command_done_event.Signal(); + } + } else { + /* Handle the event. */ + IEventHandler::ToEventHandler(event_holder).HandleEvent(); + } + } + } + +} diff --git a/libraries/libstratosphere/source/ddsf/ddsf_memory_api.cpp b/libraries/libstratosphere/source/ddsf/ddsf_memory_api.cpp new file mode 100644 index 000000000..d81155306 --- /dev/null +++ b/libraries/libstratosphere/source/ddsf/ddsf_memory_api.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ddsf { + + namespace { + + constinit ams::MemoryResource *g_memory_resource = nullptr; + constinit ams::MemoryResource *g_device_code_entry_holder_memory_resource = nullptr; + + } + + void SetMemoryResource(ams::MemoryResource *mr) { + AMS_ASSERT(g_memory_resource == nullptr); + g_memory_resource = mr; + AMS_ASSERT(g_memory_resource != nullptr); + } + + ams::MemoryResource *GetMemoryResource() { + AMS_ASSERT(g_memory_resource != nullptr); + return g_memory_resource; + } + + void SetDeviceCodeEntryHolderMemoryResource(ams::MemoryResource *mr) { + AMS_ASSERT(g_device_code_entry_holder_memory_resource == nullptr); + g_device_code_entry_holder_memory_resource = mr; + AMS_ASSERT(g_device_code_entry_holder_memory_resource != nullptr); + } + + ams::MemoryResource *GetDeviceCodeEntryHolderMemoryResource() { + AMS_ASSERT(g_device_code_entry_holder_memory_resource != nullptr); + return g_device_code_entry_holder_memory_resource; + } + +} diff --git a/libraries/libstratosphere/source/ddsf/ddsf_session_api.cpp b/libraries/libstratosphere/source/ddsf/ddsf_session_api.cpp new file mode 100644 index 000000000..48a265f62 --- /dev/null +++ b/libraries/libstratosphere/source/ddsf/ddsf_session_api.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ddsf { + + Result OpenSession(IDevice *device, ISession *session, AccessMode access_mode) { + /* Check pre-conditions. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(session != nullptr); + AMS_ASSERT(!session->IsOpen()); + + /* Attack the session to the device. */ + session->AttachDevice(device, access_mode); + auto session_guard = SCOPE_GUARD { session->DetachDevice(); }; + + /* Attach the device to the session. */ + R_TRY(device->AttachSession(session)); + + /* We succeeded. */ + session_guard.Cancel(); + return ResultSuccess(); + } + + void CloseSession(ISession *session) { + /* Check pre-conditions. */ + AMS_ASSERT(session != nullptr); + + /* Detach the device from the session. */ + session->GetDevice().DetachSession(session); + + /* Detach the session from the device. */ + session->DetachDevice(); + } + +} diff --git a/libraries/libstratosphere/source/os/os_sdk_condition_variable.cpp b/libraries/libstratosphere/source/os/os_sdk_condition_variable.cpp new file mode 100644 index 000000000..78bff6612 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_sdk_condition_variable.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/os_thread_manager.hpp" +#include "impl/os_timeout_helper.hpp" + +namespace ams::os { + + void SdkConditionVariableType::Wait(SdkMutexType &mutex) { + /* Check that we own the mutex. */ + AMS_ABORT_UNLESS(os::IsSdkMutexLockedByCurrentThread(std::addressof(mutex))); + + /* Wait on the mutex. */ + GetReference(this->_storage).Wait(GetPointer(mutex._storage)); + } + + bool SdkConditionVariableType::TimedWait(SdkMutexType &mutex, TimeSpan timeout) { + /* Check preconditions. */ + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + AMS_ABORT_UNLESS(os::IsSdkMutexLockedByCurrentThread(std::addressof(mutex))); + + /* Handle zero timeout by unlocking and re-locking. */ + if (timeout == TimeSpan(0)) { + GetReference(mutex._storage).Leave(); + GetReference(mutex._storage).Enter(); + return false; + } + + /* Handle timed wait. */ + impl::TimeoutHelper timeout_helper(timeout); + auto status = GetReference(this->_storage).TimedWait(GetPointer(mutex._storage), timeout_helper); + return status == ConditionVariableStatus::Success; + } + +} diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 596114b95..e225f4db5 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/ddsf_results.hpp b/libraries/libvapours/include/vapours/results/ddsf_results.hpp new file mode 100644 index 000000000..9ea4d667b --- /dev/null +++ b/libraries/libvapours/include/vapours/results/ddsf_results.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::ddsf { + + R_DEFINE_NAMESPACE_RESULT_MODULE(30); + + R_DEFINE_ERROR_RESULT(OutOfResource, 1); + R_DEFINE_ERROR_RESULT(NotSupported, 2); + R_DEFINE_ERROR_RESULT(InvalidArgument, 3); + R_DEFINE_ERROR_RESULT(PermissionDenied, 4); + R_DEFINE_ERROR_RESULT(AccessModeDenied, 5); + R_DEFINE_ERROR_RESULT(DeviceCodeNotFound, 6); + +}