mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
ddsf: implement namespace
This commit is contained in:
parent
d2e530c2aa
commit
5b617f4d2f
23 changed files with 1578 additions and 5 deletions
|
@ -43,6 +43,7 @@
|
|||
#include <stratosphere/boot2.hpp>
|
||||
#include <stratosphere/capsrv.hpp>
|
||||
#include <stratosphere/cfg.hpp>
|
||||
#include <stratosphere/ddsf.hpp>
|
||||
#include <stratosphere/dmnt.hpp>
|
||||
#include <stratosphere/erpt.hpp>
|
||||
#include <stratosphere/err.hpp>
|
||||
|
|
|
@ -13,7 +13,15 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stratosphere/ddsf/ddsf_types.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_i_castable.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_i_session.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_i_device.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_i_driver.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_device_code_entry.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_device_code_entry_manager.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_i_event_handler.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_event_handler_manager.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_memory_api.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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
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<DeviceCodeEntryHolder, util::IntrusiveListMemberTraitsDeferredAssert<&DeviceCodeEntryHolder::list_node>>;
|
||||
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());
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_types.hpp>
|
||||
#include <stratosphere/ddsf/impl/ddsf_for_each.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_device_code_entry.hpp>
|
||||
|
||||
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<typename F>
|
||||
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<typename F>
|
||||
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());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_i_event_handler.hpp>
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_types.hpp>
|
||||
#include <stratosphere/ddsf/impl/ddsf_type_tag.hpp>
|
||||
|
||||
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<typename T>
|
||||
constexpr ALWAYS_INLINE void AssertCastableTo() const {
|
||||
AMS_ASSERT(this->IsCastableTo<T>());
|
||||
}
|
||||
public:
|
||||
template<typename T>
|
||||
constexpr bool IsCastableTo() const {
|
||||
return this->GetTypeTag().DerivesFrom(T::s_ams_ddsf_castable_type_tag);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T &SafeCastTo() {
|
||||
this->AssertCastableTo<T>();
|
||||
return static_cast<T &>(*this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T &SafeCastTo() const {
|
||||
this->AssertCastableTo<T>();
|
||||
return static_cast<T &>(*this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T *SafeCastToPointer() {
|
||||
this->AssertCastableTo<T>();
|
||||
return static_cast<T *>(this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T *SafeCastToPointer() const {
|
||||
this->AssertCastableTo<T>();
|
||||
return static_cast<T *>(this);
|
||||
}
|
||||
|
||||
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||
|
||||
constexpr const char *GetClassName() const {
|
||||
return this->GetTypeTag().GetClassName();
|
||||
}
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/os/os_sdk_mutex.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_types.hpp>
|
||||
#include <stratosphere/ddsf/impl/ddsf_for_each.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_i_castable.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_i_session.hpp>
|
||||
|
||||
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<IDevice, util::IntrusiveListMemberTraitsDeferredAssert<&IDevice::list_node>>;
|
||||
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<typename F>
|
||||
Result ForEachSession(F f, bool return_on_fail) {
|
||||
return impl::ForEach(this->session_list_lock, this->session_list, f, return_on_fail);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
Result ForEachSession(F f, bool return_on_fail) const {
|
||||
return impl::ForEach(this->session_list_lock, this->session_list, f, return_on_fail);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
int ForEachSession(F f) {
|
||||
return impl::ForEach(this->session_list_lock, this->session_list, f);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
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());
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/os/os_sdk_mutex.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_types.hpp>
|
||||
#include <stratosphere/ddsf/impl/ddsf_for_each.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_i_castable.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_i_device.hpp>
|
||||
|
||||
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<IDriver, util::IntrusiveListMemberTraitsDeferredAssert<&IDriver::list_node>>;
|
||||
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<typename F>
|
||||
Result ForEachDevice(F f, bool return_on_fail) {
|
||||
return impl::ForEach(this->device_list_lock, this->device_list, f, return_on_fail);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
Result ForEachDevice(F f, bool return_on_fail) const {
|
||||
return impl::ForEach(this->device_list_lock, this->device_list, f, return_on_fail);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
int ForEachDevice(F f) {
|
||||
return impl::ForEach(this->device_list_lock, this->device_list, f);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
int ForEachDevice(F f) const {
|
||||
return impl::ForEach(this->device_list_lock, this->device_list, f);
|
||||
}
|
||||
};
|
||||
static_assert(IDriver::ListTraits::IsValid());
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/os.hpp>
|
||||
|
||||
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<IEventHandler *>(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<typename T>
|
||||
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<uintptr_t>(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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_types.hpp>
|
||||
#include <stratosphere/ddsf/impl/ddsf_for_each.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_i_castable.hpp>
|
||||
|
||||
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<ISession, util::IntrusiveListMemberTraitsDeferredAssert<&ISession::list_node>>;
|
||||
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());
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_device_code_entry.hpp>
|
||||
|
||||
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();
|
||||
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::ddsf::impl {
|
||||
|
||||
template<typename Lock, typename List, typename F>
|
||||
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<typename List, typename F, typename Lock>
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_types.hpp>
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -28,6 +28,7 @@
|
|||
#include <stratosphere/os/os_mutex.hpp>
|
||||
#include <stratosphere/os/os_condition_variable.hpp>
|
||||
#include <stratosphere/os/os_sdk_mutex.hpp>
|
||||
#include <stratosphere/os/os_sdk_condition_variable.hpp>
|
||||
#include <stratosphere/os/os_rw_lock.hpp>
|
||||
#include <stratosphere/os/os_transfer_memory.hpp>
|
||||
#include <stratosphere/os/os_semaphore.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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/os/os_sdk_mutex.hpp>
|
||||
#include <stratosphere/os/impl/os_internal_condition_variable.hpp>
|
||||
|
||||
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<SdkConditionVariableType>::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();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
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<DeviceCodeEntryHolder *>(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();
|
||||
}
|
||||
|
||||
}
|
215
libraries/libstratosphere/source/ddsf/ddsf_event_handler.cpp
Normal file
215
libraries/libstratosphere/source/ddsf/ddsf_event_handler.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
49
libraries/libstratosphere/source/ddsf/ddsf_memory_api.cpp
Normal file
49
libraries/libstratosphere/source/ddsf/ddsf_memory_api.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
49
libraries/libstratosphere/source/ddsf/ddsf_session_api.cpp
Normal file
49
libraries/libstratosphere/source/ddsf/ddsf_session_api.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
|
@ -26,6 +26,7 @@
|
|||
#include <vapours/results/cal_results.hpp>
|
||||
#include <vapours/results/capsrv_results.hpp>
|
||||
#include <vapours/results/creport_results.hpp>
|
||||
#include <vapours/results/ddsf_results.hpp>
|
||||
#include <vapours/results/debug_results.hpp>
|
||||
#include <vapours/results/dmnt_results.hpp>
|
||||
#include <vapours/results/erpt_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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/results/results_common.hpp>
|
||||
|
||||
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);
|
||||
|
||||
}
|
Loading…
Reference in a new issue