ddsf: implement namespace
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user