From bb48e33074577825c609d71a9ad49e7587f2c05b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 5 Dec 2018 02:11:20 -0800 Subject: [PATCH] tma: Skeleton Service/Task/TaskList classes. --- stratosphere/libstratosphere | 2 +- stratosphere/tma/source/tma_conn_packet.hpp | 6 +- .../tma/source/tma_conn_service_ids.hpp | 9 +- .../tma/source/tma_conn_usb_connection.cpp | 9 +- stratosphere/tma/source/tma_main.cpp | 3 +- stratosphere/tma/source/tma_service.cpp | 32 +++ stratosphere/tma/source/tma_service.hpp | 43 ++++ stratosphere/tma/source/tma_task.cpp | 29 +++ stratosphere/tma/source/tma_task.hpp | 74 +++++++ stratosphere/tma/source/tma_task_list.cpp | 183 ++++++++++++++++++ stratosphere/tma/source/tma_task_list.hpp | 49 +++++ 11 files changed, 428 insertions(+), 11 deletions(-) create mode 100644 stratosphere/tma/source/tma_service.cpp create mode 100644 stratosphere/tma/source/tma_service.hpp create mode 100644 stratosphere/tma/source/tma_task.cpp create mode 100644 stratosphere/tma/source/tma_task.hpp create mode 100644 stratosphere/tma/source/tma_task_list.cpp create mode 100644 stratosphere/tma/source/tma_task_list.hpp diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 05015b935..fa37b70b0 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 05015b9354d3df80e0836aa95d1d4dcfc2aef4b7 +Subproject commit fa37b70b0eca93be04e18636db25c9443e00d03b diff --git a/stratosphere/tma/source/tma_conn_packet.hpp b/stratosphere/tma/source/tma_conn_packet.hpp index 18537c119..0b2593876 100644 --- a/stratosphere/tma/source/tma_conn_packet.hpp +++ b/stratosphere/tma/source/tma_conn_packet.hpp @@ -117,12 +117,12 @@ class TmaPacket { return MaxPacketSize - this->offset; } - void SetServiceId(TmaService srv) { + void SetServiceId(TmaServiceId srv) { GetHeader()->service_id = static_cast(srv); } - TmaService GetServiceId() const { - return static_cast(GetHeader()->service_id); + TmaServiceId GetServiceId() const { + return static_cast(GetHeader()->service_id); } void SetTaskId(u32 id) { diff --git a/stratosphere/tma/source/tma_conn_service_ids.hpp b/stratosphere/tma/source/tma_conn_service_ids.hpp index 1120d81ec..eb0543d3d 100644 --- a/stratosphere/tma/source/tma_conn_service_ids.hpp +++ b/stratosphere/tma/source/tma_conn_service_ids.hpp @@ -32,7 +32,7 @@ static constexpr u32 HashServiceName(const char *name) { return h ^ len; } -enum class TmaService : u32 { +enum class TmaServiceId : u32 { Invalid = 0, /* Special nodes, for facilitating connection over USB. */ @@ -44,3 +44,10 @@ enum class TmaService : u32 { TestService = HashServiceName("AtmosphereTestService"), /* Temporary service, will be used to debug communications. */ }; + +static constexpr bool IsMetaService(TmaServiceId id) { + return id == TmaServiceId::UsbQueryTarget || + id == TmaServiceId::UsbSendHostInfo || + id == TmaServiceId::UsbConnect || + id == TmaServiceId::UsbDisconnect; +} diff --git a/stratosphere/tma/source/tma_conn_usb_connection.cpp b/stratosphere/tma/source/tma_conn_usb_connection.cpp index 75bf1a2a4..ba6ecd349 100644 --- a/stratosphere/tma/source/tma_conn_usb_connection.cpp +++ b/stratosphere/tma/source/tma_conn_usb_connection.cpp @@ -72,7 +72,6 @@ void TmaUsbConnection::SendThreadFunc(void *arg) { void TmaUsbConnection::RecvThreadFunc(void *arg) { TmaUsbConnection *this_ptr = reinterpret_cast(arg); TmaConnResult res = TmaConnResult::Success; - u64 i = 0; this_ptr->SetConnected(true); while (res == TmaConnResult::Success) { @@ -83,7 +82,7 @@ void TmaUsbConnection::RecvThreadFunc(void *arg) { if (res == TmaConnResult::Success) { switch (packet->GetServiceId()) { - case TmaService::UsbQueryTarget: { + case TmaServiceId::UsbQueryTarget: { this_ptr->SetConnected(false); res = this_ptr->SendQueryReply(packet); @@ -93,7 +92,7 @@ void TmaUsbConnection::RecvThreadFunc(void *arg) { } } break; - case TmaService::UsbSendHostInfo: { + case TmaServiceId::UsbSendHostInfo: { struct { u32 version; u32 sleeping; @@ -105,7 +104,7 @@ void TmaUsbConnection::RecvThreadFunc(void *arg) { } } break; - case TmaService::UsbConnect: { + case TmaServiceId::UsbConnect: { res = this_ptr->SendQueryReply(packet); if (res == TmaConnResult::Success) { @@ -114,7 +113,7 @@ void TmaUsbConnection::RecvThreadFunc(void *arg) { } } break; - case TmaService::UsbDisconnect: { + case TmaServiceId::UsbDisconnect: { this_ptr->SetConnected(false); this_ptr->OnDisconnected(); diff --git a/stratosphere/tma/source/tma_main.cpp b/stratosphere/tma/source/tma_main.cpp index 8fc308444..d1b179d4c 100644 --- a/stratosphere/tma/source/tma_main.cpp +++ b/stratosphere/tma/source/tma_main.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include "tma_conn_usb_connection.hpp" @@ -64,7 +65,7 @@ void __appInit(void) { fatalSimple(rc); } - CheckAtmosphereVersion(); + CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); } void __appExit(void) { diff --git a/stratosphere/tma/source/tma_service.cpp b/stratosphere/tma/source/tma_service.cpp new file mode 100644 index 000000000..963d6e6d5 --- /dev/null +++ b/stratosphere/tma/source/tma_service.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 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 +#include "tma_service.hpp" + +u32 TmaService::GetNextTaskId() { + /* TODO: Service Manager */ + return 0; +} + +void TmaService::OnSleep() { + /* Default service does nothing here. */ +} + +void TmaService::OnWake() { + /* Default service does nothing here. */ +} \ No newline at end of file diff --git a/stratosphere/tma/source/tma_service.hpp b/stratosphere/tma/source/tma_service.hpp new file mode 100644 index 000000000..17ab8215b --- /dev/null +++ b/stratosphere/tma/source/tma_service.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 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 "tma_conn_service_ids.hpp" +#include "tma_conn_packet.hpp" +#include "tma_task.hpp" + +class TmaServiceManager; + +class TmaService { + protected: + TmaServiceManager *manager; + const char *service_name; + const TmaServiceId id; + protected: + u32 GetNextTaskId(); + virtual TmaTask *NewTask(TmaPacket *packet) = 0; + + virtual void OnSleep(); + virtual void OnWake(); + public: + TmaService(TmaServiceManager *m, const char *n) : manager(m), service_name(n), id(static_cast(HashServiceName(this->service_name))) { } + virtual ~TmaService() { } + + TmaServiceId GetServiceId() const { return this->id; } +}; diff --git a/stratosphere/tma/source/tma_task.cpp b/stratosphere/tma/source/tma_task.cpp new file mode 100644 index 000000000..5c9c863b6 --- /dev/null +++ b/stratosphere/tma/source/tma_task.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 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 +#include "tma_task.hpp" + +void TmaTask::Complete() { + /* TODO: Service manager */ + this->state = TmaTaskState::Complete; +} + +void TmaTask::Cancel() { + /* TODO: Service manager */ + this->state = TmaTaskState::Canceled; +} \ No newline at end of file diff --git a/stratosphere/tma/source/tma_task.hpp b/stratosphere/tma/source/tma_task.hpp new file mode 100644 index 000000000..9e115893f --- /dev/null +++ b/stratosphere/tma/source/tma_task.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018 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 "tma_conn_service_ids.hpp" +#include "tma_conn_packet.hpp" + +enum class TmaTaskState : u32 { + InProgress, + Complete, + Canceled, +}; + +class TmaServiceManager; + +class TmaTask { + public: + static constexpr u32 MaxPriority = 15; + static constexpr u32 NumPriorities = MaxPriority + 1; + protected: + TmaServiceManager *manager; + u32 priority = 0; + TmaServiceId service_id = TmaServiceId::Invalid; + u32 task_id = 0; + u32 command = 0; + TmaTaskState state = TmaTaskState::InProgress; + HosSignal signal; + bool owned_by_task_list = true; + bool sleep_allowed = true; + public: + TmaTask(TmaServiceManager *m) : manager(m) { } + virtual ~TmaTask() { } + + u32 GetPriority() const { return this->priority; } + TmaServiceId GetServiceId() const { return this->service_id; } + u32 GetTaskId() const { return this->task_id; } + u32 GetCommand() const { return this->command; } + TmaTaskState GetState() const { return this->state; } + bool GetOwnedByTaskList() const { return this->owned_by_task_list; } + bool GetSleepAllowed() const { return this->sleep_allowed; } + + void SetPriority(u32 p) { this->priority = p; } + void SetServiceId(TmaServiceId s) { this->service_id = s; } + void SetTaskId(u32 i) { this->task_id = i; } + void SetCommand(u32 c) { this->command = c; } + void SetOwnedByTaskList(bool o) { this->owned_by_task_list = o; } + void SetSleepAllowed(bool a) { this->sleep_allowed = a; } + + void Signal() { this->signal.Signal(); } + void ResetSignal() { this->signal.Reset(); } + + void Complete(); + void Cancel(); + + virtual void OnStart(TmaPacket *packet) = 0; + virtual void OnReceivePacket(TmaPacket *packet) = 0; + virtual void OnSendPacket(TmaPacket *packet) = 0; +}; diff --git a/stratosphere/tma/source/tma_task_list.cpp b/stratosphere/tma/source/tma_task_list.cpp new file mode 100644 index 000000000..2c8ac5b48 --- /dev/null +++ b/stratosphere/tma/source/tma_task_list.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2018 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 +#include +#include "tma_task_list.hpp" + +TmaTask *TmaTaskList::GetById(u32 task_id) const { + for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) { + for (auto task : this->tasks[i]) { + if (task->GetTaskId() == task_id) { + return task; + } + } + } + return nullptr; +} + +u32 TmaTaskList::GetNumTasks() const { + std::scoped_lock lk(this->lock); + u32 count = 0; + + for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) { + count += this->tasks[i].size(); + } + + return count; +} + +u32 TmaTaskList::GetNumSleepingTasks() const { + std::scoped_lock lk(this->lock); + u32 count = 0; + + for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) { + count += this->sleeping_tasks[i].size(); + } + + return count; +} + +bool TmaTaskList::SendPacket(bool connected, TmaPacket *packet) { + std::scoped_lock lk(this->lock); + + TmaTask *target_task = nullptr; + + /* This loop both finds a target task, and cleans up finished tasks. */ + for (u32 i = 0; i < TmaTask::NumPriorities; i++) { + auto it = this->tasks[i].begin(); + while (it != this->tasks[i].end()) { + auto task = *it; + switch (task->GetState()) { + case TmaTaskState::InProgress: + it++; + if (target_task == nullptr) { + if (connected || IsMetaService(task->GetServiceId())) { + target_task = nullptr; + } + } + break; + case TmaTaskState::Complete: + case TmaTaskState::Canceled: + it = this->tasks[i].erase(it); + if (task->GetOwnedByTaskList()) { + delete task; + } else { + task->Signal(); + } + break; + default: + /* TODO: Panic to fatal? */ + std::abort(); + } + } + } + + if (target_task) { + /* Setup packet. */ + packet->SetContinuation(true); + packet->SetServiceId(target_task->GetServiceId()); + packet->SetTaskId(target_task->GetTaskId()); + packet->SetCommand(target_task->GetCommand()); + packet->ClearOffset(); + + /* Actually handle packet send. */ + target_task->OnSendPacket(packet); + } + + return target_task != nullptr; +} + +bool TmaTaskList::ReceivePacket(TmaPacket *packet) { + std::scoped_lock lk(this->lock); + + auto task = this->GetById(packet->GetTaskId()); + if (task != nullptr) { + task->OnReceivePacket(packet); + } + return task != nullptr; +} + +void TmaTaskList::Add(TmaTask *task) { + std::scoped_lock lk(this->lock); + + this->tasks[task->GetPriority()].push_back(task); +} + +void TmaTaskList::Remove(TmaTask *task) { + const auto priority = task->GetPriority(); + + /* Nintendo iterates over all lists instead of just the correct one. */ + /* TODO: Is there actually any reason to do that? */ + auto ind = std::find(this->tasks[priority].begin(), this->tasks[priority].end(), task); + if (ind != this->tasks[priority].end()) { + this->tasks[priority].erase(ind); + return; + } + + /* TODO: Panic to fatal? */ + std::abort(); +} + +void TmaTaskList::Cancel(u32 task_id) { + std::scoped_lock lk(this->lock); + + auto task = this->GetById(task_id); + if (task != nullptr) { + task->Cancel(); + } +} + +void TmaTaskList::CancelAll() { + std::scoped_lock lk(this->lock); + + for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) { + for (auto task : this->tasks[i]) { + task->Cancel(); + } + } +} + +void TmaTaskList::Sleep() { + std::scoped_lock lk(this->lock); + + for (u32 i = 0; i < TmaTask::NumPriorities; i++) { + auto it = this->tasks[i].begin(); + while (it != this->tasks[i].end()) { + auto task = *it; + if (task->GetSleepAllowed()) { + it = this->tasks[i].erase(it); + this->sleeping_tasks[i].push_back(task); + } else { + it++; + } + } + } +} + +void TmaTaskList::Wake() { + std::scoped_lock lk(this->lock); + + for (u32 i = 0; i < TmaTask::NumPriorities; i++) { + auto it = this->sleeping_tasks[i].begin(); + while (it != this->sleeping_tasks[i].end()) { + auto task = *it; + it = this->sleeping_tasks[i].erase(it); + this->tasks[i].push_back(task); + } + } +} diff --git a/stratosphere/tma/source/tma_task_list.hpp b/stratosphere/tma/source/tma_task_list.hpp new file mode 100644 index 000000000..bfad0e6ca --- /dev/null +++ b/stratosphere/tma/source/tma_task_list.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 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 "tma_conn_service_ids.hpp" +#include "tma_task.hpp" + +class TmaTaskList { + private: + mutable HosMutex lock; + std::vector tasks[TmaTask::NumPriorities]; + std::vector sleeping_tasks[TmaTask::NumPriorities]; + private: + void Remove(TmaTask *task); + TmaTask *GetById(u32 task_id) const; + public: + TmaTaskList() { } + virtual ~TmaTaskList() { } + + u32 GetNumTasks() const; + u32 GetNumSleepingTasks() const; + + bool SendPacket(bool connected, TmaPacket *packet); + bool ReceivePacket(TmaPacket *packet); + + void Add(TmaTask *task); + void Cancel(u32 task_id); + void CancelAll(); + + void Sleep(); + void Wake(); +}; \ No newline at end of file