mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-23 04:41:12 +00:00
tma: first pass at TmaServiceManager
This commit is contained in:
parent
bb48e33074
commit
bf7dc84893
11 changed files with 595 additions and 22 deletions
|
@ -17,25 +17,23 @@
|
|||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_conn_connection.hpp"
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
/* Packet management. */
|
||||
TmaPacket *TmaConnection::AllocateSendPacket() {
|
||||
/* TODO: Service Manager. */
|
||||
return new TmaPacket();
|
||||
return this->service_manager->AllocateSendPacket();
|
||||
}
|
||||
|
||||
TmaPacket *TmaConnection::AllocateRecvPacket() {
|
||||
/* TODO: Service Manager. */
|
||||
return new TmaPacket();
|
||||
return this->service_manager->AllocateRecvPacket();
|
||||
}
|
||||
|
||||
void TmaConnection::FreePacket(TmaPacket *packet) {
|
||||
/* TODO: Service Manager. */
|
||||
delete packet;
|
||||
this->service_manager->FreePacket(packet);
|
||||
}
|
||||
|
||||
void TmaConnection::OnReceivePacket(TmaPacket *packet) {
|
||||
/* TODO: Service Manager. */
|
||||
this->service_manager->OnReceivePacket(packet);
|
||||
}
|
||||
|
||||
void TmaConnection::OnDisconnected() {
|
||||
|
@ -43,7 +41,9 @@ void TmaConnection::OnDisconnected() {
|
|||
std::abort();
|
||||
}
|
||||
|
||||
/* TODO: Service Manager. */
|
||||
if (this->service_manager != nullptr) {
|
||||
this->service_manager->OnDisconnect();
|
||||
}
|
||||
|
||||
this->has_woken_up = false;
|
||||
this->OnConnectionEvent(ConnectionEvent::Disconnected);
|
||||
|
@ -56,9 +56,9 @@ void TmaConnection::OnConnectionEvent(ConnectionEvent event) {
|
|||
}
|
||||
|
||||
void TmaConnection::CancelTasks() {
|
||||
/* TODO: Service Manager. */
|
||||
this->service_manager->CancelTasks();
|
||||
}
|
||||
|
||||
void TmaConnection::Tick() {
|
||||
/* TODO: Service Manager. */
|
||||
this->service_manager->Tick();
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ enum class ConnectionEvent : u32 {
|
|||
Disconnected
|
||||
};
|
||||
|
||||
|
||||
class TmaServiceManager;
|
||||
|
||||
class TmaConnection {
|
||||
protected:
|
||||
HosMutex lock;
|
||||
|
@ -33,6 +36,7 @@ class TmaConnection {
|
|||
void *connection_event_arg = nullptr;
|
||||
bool has_woken_up = false;
|
||||
bool is_initialized = false;
|
||||
TmaServiceManager *service_manager = nullptr;
|
||||
protected:
|
||||
void OnReceivePacket(TmaPacket *packet);
|
||||
void OnDisconnected();
|
||||
|
@ -66,11 +70,17 @@ class TmaConnection {
|
|||
}
|
||||
}
|
||||
|
||||
void SetServiceManager(TmaServiceManager *manager) { this->service_manager = manager; }
|
||||
|
||||
/* Packet management. */
|
||||
TmaPacket *AllocateSendPacket();
|
||||
TmaPacket *AllocateRecvPacket();
|
||||
void FreePacket(TmaPacket *packet);
|
||||
|
||||
/* Sleep management. */
|
||||
bool HasWokenUp() const { return this->has_woken_up; }
|
||||
void SetWokenUp(bool woke) { this->has_woken_up = woke; }
|
||||
|
||||
/* For sub-interfaces to implement, connection management. */
|
||||
virtual void StartListening() { }
|
||||
virtual void StopListening() { }
|
||||
|
|
|
@ -43,6 +43,7 @@ class TmaPacket {
|
|||
private:
|
||||
std::unique_ptr<u8[]> buffer = std::make_unique<u8[]>(MaxPacketSize);
|
||||
u32 offset = 0;
|
||||
HosMessageQueue *free_queue = nullptr;
|
||||
|
||||
Header *GetHeader() const {
|
||||
return reinterpret_cast<Header *>(buffer.get());
|
||||
|
@ -95,6 +96,14 @@ class TmaPacket {
|
|||
}
|
||||
}
|
||||
|
||||
HosMessageQueue *GetFreeQueue() const {
|
||||
return this->free_queue;
|
||||
}
|
||||
|
||||
void SetFreeQueue(HosMessageQueue *queue) {
|
||||
this->free_queue = queue;
|
||||
}
|
||||
|
||||
void SetChecksums() {
|
||||
Header *hdr = GetHeader();
|
||||
if (hdr->body_len) {
|
||||
|
@ -161,6 +170,10 @@ class TmaPacket {
|
|||
this->offset = 0;
|
||||
}
|
||||
|
||||
void SetBodyLength() {
|
||||
GetHeader()->body_len = this->offset;
|
||||
}
|
||||
|
||||
TmaConnResult Write(const void *data, size_t size) {
|
||||
if (size > GetBodyAvailableLength()) {
|
||||
return TmaConnResult::PacketOverflow;
|
||||
|
|
|
@ -88,7 +88,7 @@ void TmaUsbConnection::RecvThreadFunc(void *arg) {
|
|||
res = this_ptr->SendQueryReply(packet);
|
||||
|
||||
if (!this_ptr->has_woken_up) {
|
||||
/* TODO: Cancel background work. */
|
||||
this_ptr->CancelTasks();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -100,7 +100,7 @@ void TmaUsbConnection::RecvThreadFunc(void *arg) {
|
|||
packet->Read<decltype(host_info)>(host_info);
|
||||
|
||||
if (!this_ptr->has_woken_up || !host_info.sleeping) {
|
||||
/* TODO: Cancel background work. */
|
||||
this_ptr->CancelTasks();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -117,7 +117,7 @@ void TmaUsbConnection::RecvThreadFunc(void *arg) {
|
|||
this_ptr->SetConnected(false);
|
||||
this_ptr->OnDisconnected();
|
||||
|
||||
/* TODO: Cancel background work. */
|
||||
this_ptr->CancelTasks();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_service.hpp"
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
u32 TmaService::GetNextTaskId() {
|
||||
/* TODO: Service Manager */
|
||||
return 0;
|
||||
return this->manager->GetNextTaskId();
|
||||
}
|
||||
|
||||
void TmaService::OnSleep() {
|
||||
|
|
|
@ -31,13 +31,13 @@ class TmaService {
|
|||
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<TmaServiceId>(HashServiceName(this->service_name))) { }
|
||||
virtual ~TmaService() { }
|
||||
|
||||
TmaServiceId GetServiceId() const { return this->id; }
|
||||
|
||||
virtual TmaTask *NewTask(TmaPacket *packet) = 0;
|
||||
virtual void OnSleep();
|
||||
virtual void OnWake();
|
||||
};
|
||||
|
|
402
stratosphere/tma/source/tma_service_manager.cpp
Normal file
402
stratosphere/tma/source/tma_service_manager.cpp
Normal file
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
TmaServiceManager::TmaServiceManager() {
|
||||
/* Set up queues */
|
||||
for (size_t i = 0; i < TmaServiceManager::PacketQueueDepth; i++) {
|
||||
TmaPacket *packet = nullptr;
|
||||
|
||||
packet = new TmaPacket();
|
||||
packet->SetFreeQueue(&this->free_send_packet_queue);
|
||||
this->free_send_packet_queue.Send(reinterpret_cast<uintptr_t>(packet));
|
||||
packet = nullptr;
|
||||
|
||||
packet = new TmaPacket();
|
||||
packet->SetFreeQueue(&this->free_recv_packet_queue);
|
||||
this->free_recv_packet_queue.Send(reinterpret_cast<uintptr_t>(packet));
|
||||
packet = nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < TmaServiceManager::WorkQueueDepth; i++) {
|
||||
this->free_work_queue.Send(reinterpret_cast<uintptr_t>(new TmaWorkItem()));
|
||||
}
|
||||
}
|
||||
|
||||
TmaServiceManager::~TmaServiceManager() {
|
||||
/* Destroy queues. */
|
||||
TmaPacket *packet = nullptr;
|
||||
while (this->free_send_packet_queue.TryReceive(reinterpret_cast<uintptr_t *>(&packet))) {
|
||||
delete packet;
|
||||
packet = nullptr;
|
||||
}
|
||||
while (this->free_recv_packet_queue.TryReceive(reinterpret_cast<uintptr_t *>(&packet))) {
|
||||
delete packet;
|
||||
packet = nullptr;
|
||||
}
|
||||
|
||||
TmaWorkItem *work_item = nullptr;
|
||||
while (this->free_work_queue.TryReceive(reinterpret_cast<uintptr_t *>(&work_item))) {
|
||||
delete work_item;
|
||||
work_item = nullptr;
|
||||
}
|
||||
while (this->work_queue.TryReceive(reinterpret_cast<uintptr_t *>(&work_item))) {
|
||||
delete work_item;
|
||||
work_item = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::Initialize() {
|
||||
this->initialized = true;
|
||||
this->work_thread.Initialize(TmaServiceManager::WorkThread, this, 0x4000, 0x26);
|
||||
this->work_thread.Start();
|
||||
}
|
||||
|
||||
void TmaServiceManager::Finalize() {
|
||||
if (this->initialized) {
|
||||
this->initialized = false;
|
||||
if (this->connection && this->connection->IsConnected()) {
|
||||
this->connection->Disconnect();
|
||||
}
|
||||
|
||||
/* Signal to work thread to end. */
|
||||
this->work_queue.Send(reinterpret_cast<uintptr_t>(nullptr));
|
||||
this->work_thread.Join();
|
||||
|
||||
/* TODO: N tells services that they have no manager here. Do we want to do that? */
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::AddWork(TmaWorkType type, TmaTask *task, TmaPacket *packet) {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
TmaWorkItem *work_item = nullptr;
|
||||
this->free_work_queue.Receive(reinterpret_cast<uintptr_t *>(&work_item));
|
||||
|
||||
work_item->task = task;
|
||||
work_item->packet = packet;
|
||||
work_item->work_type = type;
|
||||
}
|
||||
|
||||
/* Packet management. */
|
||||
TmaConnResult TmaServiceManager::SendPacket(TmaPacket *packet) {
|
||||
TmaConnResult res = TmaConnResult::Disconnected;
|
||||
|
||||
if (this->connection != nullptr) {
|
||||
res = this->connection->SendPacket(packet);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void TmaServiceManager::OnReceivePacket(TmaPacket *packet) {
|
||||
this->AddWork(TmaWorkType::ReceivePacket, nullptr, packet);
|
||||
}
|
||||
|
||||
TmaPacket *TmaServiceManager::AllocateSendPacket() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
TmaPacket *packet = nullptr;
|
||||
this->free_send_packet_queue.Receive(reinterpret_cast<uintptr_t *>(&packet));
|
||||
|
||||
packet->ClearOffset();
|
||||
packet->SetBodyLength();
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
TmaPacket *TmaServiceManager::AllocateRecvPacket() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
TmaPacket *packet = nullptr;
|
||||
this->free_recv_packet_queue.Receive(reinterpret_cast<uintptr_t *>(&packet));
|
||||
|
||||
packet->ClearOffset();
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
void TmaServiceManager::FreePacket(TmaPacket *packet) {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (packet != nullptr) {
|
||||
packet->GetFreeQueue()->Send(reinterpret_cast<uintptr_t>(packet));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Service/task management. */
|
||||
TmaService *TmaServiceManager::GetServiceById(TmaServiceId id) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
for (auto srv : this->services) {
|
||||
if (srv->GetServiceId() == id) {
|
||||
return srv;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TmaServiceManager::AddService(TmaService *service) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
this->services.push_back(service);
|
||||
}
|
||||
|
||||
void TmaServiceManager::AddTask(TmaTask *task, TmaPacket *packet) {
|
||||
this->AddWork(TmaWorkType::NewTask, task, packet);
|
||||
}
|
||||
|
||||
void TmaServiceManager::FreeTask(TmaTask *task) {
|
||||
this->AddWork(TmaWorkType::FreeTask, task, nullptr);
|
||||
}
|
||||
|
||||
void TmaServiceManager::CancelTask(u32 task_id) {
|
||||
if (this->initialized) {
|
||||
this->task_list.Cancel(task_id);
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::CancelTasks() {
|
||||
if (this->initialized) {
|
||||
this->task_list.CancelAll();
|
||||
}
|
||||
}
|
||||
|
||||
u32 TmaServiceManager::GetNextTaskId() {
|
||||
while (true) {
|
||||
u32 id;
|
||||
{
|
||||
/* N only uses 16 bits for the task id. We'll use 24. */
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
id = (this->next_task_id++) & 0xFFFFFF;
|
||||
}
|
||||
|
||||
if (this->task_list.IsIdFree(id)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Connection management. */
|
||||
void TmaServiceManager::Tick() {
|
||||
this->AddWork(TmaWorkType::Tick, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void TmaServiceManager::SetConnection(TmaConnection *conn) {
|
||||
this->connection = conn;
|
||||
}
|
||||
|
||||
void TmaServiceManager::OnDisconnect() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (!this->GetAsleep()) {
|
||||
this->disconnect_signal.Reset();
|
||||
|
||||
this->AddWork(TmaWorkType::Disconnect, nullptr, nullptr);
|
||||
|
||||
/* TODO: why does N wait with a timeout of zero here? */
|
||||
this->disconnect_signal.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::Sleep() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (!this->GetAsleep()) {
|
||||
this->wake_signal.Reset();
|
||||
this->sleep_signal.Reset();
|
||||
|
||||
/* Tell the work thread to stall, wait for ACK. */
|
||||
this->AddWork(TmaWorkType::Sleep, nullptr, nullptr);
|
||||
this->sleep_signal.Wait();
|
||||
|
||||
this->SetAsleep(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::Wake(TmaConnection *conn) {
|
||||
if (this->connection != nullptr) {
|
||||
std::abort();
|
||||
}
|
||||
if (this->GetAsleep()) {
|
||||
this->connection = conn;
|
||||
this->connection->SetWokenUp(true);
|
||||
this->connection->SetServiceManager(this);
|
||||
/* Tell the work thread to resume. */
|
||||
this->wake_signal.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
bool TmaServiceManager::GetConnected() const {
|
||||
return this->connection != nullptr && this->connection->IsConnected();
|
||||
}
|
||||
|
||||
/* Work thread. */
|
||||
void TmaServiceManager::WorkThread(void *_this) {
|
||||
TmaServiceManager *this_ptr = reinterpret_cast<TmaServiceManager *>(_this);
|
||||
if (!this_ptr->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
/* Receive a work item. */
|
||||
TmaWorkItem *work_item = nullptr;
|
||||
this_ptr->work_queue.Receive(reinterpret_cast<uintptr_t *>(&work_item));
|
||||
|
||||
if (work_item == nullptr) {
|
||||
/* We're done. */
|
||||
this_ptr->task_list.CancelAll();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (work_item->work_type) {
|
||||
case TmaWorkType::Tick:
|
||||
/* HandleTickWork called unconditionally. */
|
||||
break;
|
||||
case TmaWorkType::NewTask:
|
||||
this_ptr->HandleNewTaskWork(work_item);
|
||||
break;
|
||||
case TmaWorkType::FreeTask:
|
||||
this_ptr->HandleFreeTaskWork(work_item);
|
||||
break;
|
||||
case TmaWorkType::ReceivePacket:
|
||||
this_ptr->HandleReceivePacketWork(work_item);
|
||||
break;
|
||||
case TmaWorkType::Disconnect:
|
||||
this_ptr->HandleDisconnectWork();
|
||||
break;
|
||||
case TmaWorkType::Sleep:
|
||||
this_ptr->HandleSleepWork();
|
||||
break;
|
||||
case TmaWorkType::None:
|
||||
default:
|
||||
std::abort();
|
||||
break;
|
||||
}
|
||||
|
||||
this_ptr->free_work_queue.Send(reinterpret_cast<uintptr_t>(work_item));
|
||||
this_ptr->HandleTickWork();
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleNewTaskWork(TmaWorkItem *work_item) {
|
||||
this->task_list.Add(work_item->task);
|
||||
if (this->GetConnected()) {
|
||||
if (work_item->packet != nullptr) {
|
||||
this->SendPacket(work_item->packet);
|
||||
}
|
||||
} else {
|
||||
work_item->task->Cancel();
|
||||
this->FreePacket(work_item->packet);
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleFreeTaskWork(TmaWorkItem *work_item) {
|
||||
delete work_item->task;
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleReceivePacketWork(TmaWorkItem *work_item) {
|
||||
/* Handle continuation packets. */
|
||||
if (work_item->packet->GetContinuation()) {
|
||||
this->task_list.ReceivePacket(work_item->packet);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make a new task for the packet. */
|
||||
TmaService *srv = this->GetServiceById(work_item->packet->GetServiceId());
|
||||
if (srv != nullptr) {
|
||||
TmaTask *task = srv->NewTask(work_item->packet);
|
||||
if (task != nullptr) {
|
||||
this->task_list.Add(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleTickWork() {
|
||||
if (this->connection == nullptr) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
/* N does this kind of manual cleanup if send isn't called. */
|
||||
/* It's pretty gross, but in lieu of a better idea... */
|
||||
bool needs_manual_cleanup = true;
|
||||
|
||||
TmaPacket *packet = nullptr;
|
||||
|
||||
while (this->connection != nullptr && this->free_send_packet_queue.TryReceive(reinterpret_cast<uintptr_t *>(&packet))) {
|
||||
needs_manual_cleanup = false;
|
||||
|
||||
if (this->task_list.SendPacket(this->GetConnected(), packet)) {
|
||||
if (this->SendPacket(packet) != TmaConnResult::Success) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this->FreePacket(packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_manual_cleanup) {
|
||||
this->task_list.CleanupDoneTasks();
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleDisconnectWork() {
|
||||
this->task_list.CancelAll();
|
||||
this->disconnect_signal.Signal();
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleSleepWork() {
|
||||
/* Put the task list to sleep. */
|
||||
this->task_list.Sleep();
|
||||
|
||||
/* Put services to sleep. */
|
||||
for (auto srv : this->services) {
|
||||
srv->OnSleep();
|
||||
}
|
||||
|
||||
/* Signal to main thread that we're sleeping. */
|
||||
this->sleep_signal.Signal();
|
||||
/* Wait for us to wake up. */
|
||||
this->wake_signal.Wait();
|
||||
|
||||
/* We're awake now... */
|
||||
|
||||
/* Wake up services. */
|
||||
for (auto srv : this->services) {
|
||||
srv->OnWake();
|
||||
}
|
||||
|
||||
/* Wake up the task list. */
|
||||
this->task_list.Wake();
|
||||
}
|
109
stratosphere/tma/source/tma_service_manager.hpp
Normal file
109
stratosphere/tma/source/tma_service_manager.hpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_service_ids.hpp"
|
||||
#include "tma_conn_packet.hpp"
|
||||
#include "tma_task.hpp"
|
||||
#include "tma_service.hpp"
|
||||
#include "tma_task_list.hpp"
|
||||
#include "tma_conn_connection.hpp"
|
||||
|
||||
enum class TmaWorkType : u32 {
|
||||
None,
|
||||
NewTask,
|
||||
FreeTask,
|
||||
ReceivePacket,
|
||||
Tick,
|
||||
Disconnect,
|
||||
Sleep,
|
||||
};
|
||||
|
||||
struct TmaWorkItem {
|
||||
TmaTask *task;
|
||||
TmaPacket *packet;
|
||||
TmaWorkType work_type;
|
||||
};
|
||||
|
||||
class TmaServiceManager {
|
||||
public:
|
||||
static constexpr size_t PacketQueueDepth = 0x8;
|
||||
static constexpr size_t WorkQueueDepth = 0x80;
|
||||
private:
|
||||
HosMutex lock;
|
||||
bool initialized = false;
|
||||
TmaTaskList task_list;
|
||||
HosThread work_thread;
|
||||
std::vector<TmaService *> services;
|
||||
TmaConnection *connection = nullptr;
|
||||
u32 next_task_id = 0;
|
||||
|
||||
/* Work queues. */
|
||||
HosMessageQueue free_send_packet_queue = HosMessageQueue(PacketQueueDepth);
|
||||
HosMessageQueue free_recv_packet_queue = HosMessageQueue(PacketQueueDepth);
|
||||
HosMessageQueue work_queue = HosMessageQueue(WorkQueueDepth);
|
||||
HosMessageQueue free_work_queue = HosMessageQueue(WorkQueueDepth);
|
||||
|
||||
/* Sleep management. */
|
||||
HosSignal disconnect_signal;
|
||||
HosSignal wake_signal;
|
||||
HosSignal sleep_signal;
|
||||
bool asleep = false;
|
||||
private:
|
||||
static void WorkThread(void *arg);
|
||||
void AddWork(TmaWorkType type, TmaTask *task, TmaPacket *packet);
|
||||
void HandleNewTaskWork(TmaWorkItem *work_item);
|
||||
void HandleFreeTaskWork(TmaWorkItem *work_item);
|
||||
void HandleReceivePacketWork(TmaWorkItem *work_item);
|
||||
void HandleTickWork();
|
||||
void HandleDisconnectWork();
|
||||
void HandleSleepWork();
|
||||
|
||||
void SetAsleep(bool s) { this->asleep = s; }
|
||||
public:
|
||||
TmaServiceManager();
|
||||
virtual ~TmaServiceManager();
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
/* Packet management. */
|
||||
TmaConnResult SendPacket(TmaPacket *packet);
|
||||
void OnReceivePacket(TmaPacket *packet);
|
||||
TmaPacket *AllocateSendPacket();
|
||||
TmaPacket *AllocateRecvPacket();
|
||||
void FreePacket(TmaPacket *packet);
|
||||
|
||||
/* Service/task management. */
|
||||
TmaService *GetServiceById(TmaServiceId id);
|
||||
void AddService(TmaService *service);
|
||||
void AddTask(TmaTask *task, TmaPacket *packet);
|
||||
void FreeTask(TmaTask *task);
|
||||
void CancelTask(u32 task_id);
|
||||
void CancelTasks();
|
||||
u32 GetNextTaskId();
|
||||
|
||||
/* Connection management. */
|
||||
void Tick();
|
||||
void SetConnection(TmaConnection *conn);
|
||||
void OnDisconnect();
|
||||
void Sleep();
|
||||
void Wake(TmaConnection *conn);
|
||||
bool GetAsleep() const { return this->asleep; }
|
||||
bool GetConnected() const;
|
||||
};
|
|
@ -17,13 +17,16 @@
|
|||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_task.hpp"
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
void TmaTask::Complete() {
|
||||
/* TODO: Service manager */
|
||||
/* TODO: Set packet state */
|
||||
this->state = TmaTaskState::Complete;
|
||||
this->manager->Tick();
|
||||
}
|
||||
|
||||
void TmaTask::Cancel() {
|
||||
/* TODO: Service manager */
|
||||
/* TODO: Set packet state */
|
||||
this->state = TmaTaskState::Canceled;
|
||||
this->manager->Tick();
|
||||
}
|
|
@ -52,6 +52,11 @@ u32 TmaTaskList::GetNumSleepingTasks() const {
|
|||
return count;
|
||||
}
|
||||
|
||||
bool TmaTaskList::IsIdFree(u32 task_id) const {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
return GetById(task_id) == nullptr;
|
||||
}
|
||||
|
||||
bool TmaTaskList::SendPacket(bool connected, TmaPacket *packet) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
|
@ -112,6 +117,36 @@ bool TmaTaskList::ReceivePacket(TmaPacket *packet) {
|
|||
return task != nullptr;
|
||||
}
|
||||
|
||||
|
||||
void TmaTaskList::CleanupDoneTasks() {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
/* Clean up all tasks in Complete/Canceled state. */
|
||||
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++;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaTaskList::Add(TmaTask *task) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
|
|
|
@ -36,10 +36,11 @@ class TmaTaskList {
|
|||
|
||||
u32 GetNumTasks() const;
|
||||
u32 GetNumSleepingTasks() const;
|
||||
bool IsIdFree(u32 task_id) const;
|
||||
|
||||
bool SendPacket(bool connected, TmaPacket *packet);
|
||||
bool ReceivePacket(TmaPacket *packet);
|
||||
|
||||
void CleanupDoneTasks();
|
||||
void Add(TmaTask *task);
|
||||
void Cancel(u32 task_id);
|
||||
void CancelAll();
|
||||
|
|
Loading…
Reference in a new issue