tma: impl helper services, cleanup hostside packets

This commit is contained in:
Michael Scire
2018-11-07 23:21:05 -08:00
parent ec8523af7c
commit 46001263f8
11 changed files with 603 additions and 18 deletions

View File

@@ -0,0 +1,64 @@
/*
* 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_conn_connection.hpp"
/* Packet management. */
TmaPacket *TmaConnection::AllocateSendPacket() {
/* TODO: Service Manager. */
return new TmaPacket();
}
TmaPacket *TmaConnection::AllocateRecvPacket() {
/* TODO: Service Manager. */
return new TmaPacket();
}
void TmaConnection::FreePacket(TmaPacket *packet) {
/* TODO: Service Manager. */
delete packet;
}
void TmaConnection::OnReceivePacket(TmaPacket *packet) {
/* TODO: Service Manager. */
}
void TmaConnection::OnDisconnected() {
if (!this->is_initialized) {
std::abort();
}
/* TODO: Service Manager. */
this->has_woken_up = false;
this->OnConnectionEvent(ConnectionEvent::Disconnected);
}
void TmaConnection::OnConnectionEvent(ConnectionEvent event) {
if (this->connection_event_callback != nullptr) {
this->connection_event_callback(this->connection_event_arg, event);
}
}
void TmaConnection::CancelTasks() {
/* TODO: Service Manager. */
}
void TmaConnection::Tick() {
/* TODO: Service Manager. */
}

View File

@@ -0,0 +1,80 @@
/*
* 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_result.hpp"
#include "tma_conn_packet.hpp"
enum class ConnectionEvent : u32 {
Connected,
Disconnected
};
class TmaConnection {
protected:
HosMutex lock;
void (*connection_event_callback)(void *, ConnectionEvent) = nullptr;
void *connection_event_arg = nullptr;
bool has_woken_up = false;
bool is_initialized = false;
protected:
void OnReceivePacket(TmaPacket *packet);
void OnDisconnected();
void OnConnectionEvent(ConnectionEvent event);
void CancelTasks();
void Tick();
public:
/* Setup */
TmaConnection() { }
virtual ~TmaConnection() { }
void Initialize() {
if (this->is_initialized) {
std::abort();
}
this->is_initialized = true;
}
void SetConnectionEventCallback(void (*callback)(void *, ConnectionEvent), void *arg) {
this->connection_event_callback = callback;
this->connection_event_arg = arg;
}
void Finalize() {
if (this->is_initialized) {
this->StopListening();
if (this->IsConnected()) {
this->Disconnect();
}
this->is_initialized = false;
}
}
/* Packet management. */
TmaPacket *AllocateSendPacket();
TmaPacket *AllocateRecvPacket();
void FreePacket(TmaPacket *packet);
/* For sub-interfaces to implement, connection management. */
virtual void StartListening() { }
virtual void StopListening() { }
virtual bool IsConnected() = 0;
virtual TmaConnResult Disconnect() = 0;
virtual TmaConnResult SendPacket(TmaPacket *packet) = 0;
};

View File

@@ -0,0 +1,155 @@
/*
* 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_conn_usb_connection.hpp"
#include "tma_usb_comms.hpp"
static HosThread g_SendThread, g_RecvThread;
TmaConnResult TmaUsbConnection::InitializeComms() {
return TmaUsbComms::Initialize();
}
TmaConnResult TmaUsbConnection::FinalizeComms() {
return TmaUsbComms::Finalize();
}
void TmaUsbConnection::ClearSendQueue() {
uintptr_t _packet;
while (this->send_queue.TryReceive(&_packet)) {
TmaPacket *packet = reinterpret_cast<TmaPacket *>(_packet);
if (packet != nullptr) {
this->FreePacket(packet);
}
}
}
void TmaUsbConnection::SendThreadFunc(void *arg) {
TmaUsbConnection *this_ptr = reinterpret_cast<TmaUsbConnection *>(arg);
TmaConnResult res = TmaConnResult::Success;
TmaPacket *packet = nullptr;
while (res == TmaConnResult::Success) {
/* Receive a packet from the send queue. */
{
uintptr_t _packet;
this_ptr->send_queue.Receive(&_packet);
packet = reinterpret_cast<TmaPacket *>(_packet);
}
if (packet != nullptr) {
/* Send the packet if we're connected. */
if (this_ptr->IsConnected()) {
res = TmaUsbComms::SendPacket(packet);
}
this_ptr->FreePacket(packet);
this_ptr->Tick();
} else {
res = TmaConnResult::Disconnected;
}
}
this_ptr->SetConnected(false);
this_ptr->OnDisconnected();
}
void TmaUsbConnection::RecvThreadFunc(void *arg) {
TmaUsbConnection *this_ptr = reinterpret_cast<TmaUsbConnection *>(arg);
TmaConnResult res = TmaConnResult::Success;
u64 i = 0;
this_ptr->SetConnected(true);
while (res == TmaConnResult::Success) {
if (!this_ptr->IsConnected()) {
break;
}
TmaPacket *packet = this_ptr->AllocateRecvPacket();
if (packet == nullptr) { std::abort(); }
res = TmaUsbComms::ReceivePacket(packet);
if (res == TmaConnResult::Success) {
TmaPacket *send_packet = this_ptr->AllocateSendPacket();
send_packet->Write<u64>(i++);
this_ptr->send_queue.Send(reinterpret_cast<uintptr_t>(send_packet));
this_ptr->FreePacket(packet);
} else {
this_ptr->FreePacket(packet);
}
}
this_ptr->SetConnected(false);
this_ptr->send_queue.Send(reinterpret_cast<uintptr_t>(nullptr));
}
void TmaUsbConnection::OnUsbStateChange(void *arg, u32 state) {
TmaUsbConnection *this_ptr = reinterpret_cast<TmaUsbConnection *>(arg);
switch (state) {
case 0:
case 6:
this_ptr->StopThreads();
break;
case 5:
this_ptr->StartThreads();
break;
}
}
void TmaUsbConnection::StartThreads() {
g_SendThread.Join();
g_RecvThread.Join();
g_SendThread.Initialize(&TmaUsbConnection::SendThreadFunc, this, 0x4000, 38);
g_RecvThread.Initialize(&TmaUsbConnection::RecvThreadFunc, this, 0x4000, 38);
this->ClearSendQueue();
g_SendThread.Start();
g_RecvThread.Start();
}
void TmaUsbConnection::StopThreads() {
TmaUsbComms::CancelComms();
g_SendThread.Join();
g_RecvThread.Join();
}
bool TmaUsbConnection::IsConnected() {
return this->is_connected;
}
TmaConnResult TmaUsbConnection::Disconnect() {
TmaUsbComms::SetStateChangeCallback(nullptr, nullptr);
this->StopThreads();
return TmaConnResult::Success;
}
TmaConnResult TmaUsbConnection::SendPacket(TmaPacket *packet) {
std::scoped_lock<HosMutex> lk(this->lock);
if (this->IsConnected()) {
this->send_queue.Send(reinterpret_cast<uintptr_t>(packet));
return TmaConnResult::Success;
} else {
this->FreePacket(packet);
this->Tick();
return TmaConnResult::Disconnected;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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_connection.hpp"
#include "tma_usb_comms.hpp"
class TmaUsbConnection : public TmaConnection {
private:
HosMessageQueue send_queue = HosMessageQueue(64);
std::atomic<bool> is_connected = false;
private:
static void SendThreadFunc(void *arg);
static void RecvThreadFunc(void *arg);
static void OnUsbStateChange(void *this_ptr, u32 state);
void ClearSendQueue();
void StartThreads();
void StopThreads();
void SetConnected(bool c) { this->is_connected = c; }
public:
static TmaConnResult InitializeComms();
static TmaConnResult FinalizeComms();
TmaUsbConnection() {
TmaUsbComms::SetStateChangeCallback(&TmaUsbConnection::OnUsbStateChange, this);
}
virtual ~TmaUsbConnection() {
this->Disconnect();
}
virtual bool IsConnected() override;
virtual TmaConnResult Disconnect() override;
virtual TmaConnResult SendPacket(TmaPacket *packet) override;
};

View File

@@ -22,14 +22,14 @@
#include <switch.h>
#include <stratosphere.hpp>
#include "tma_usb_comms.hpp"
#include "tma_conn_usb_connection.hpp"
extern "C" {
extern u32 __start__;
u32 __nx_applet_type = AppletType_None;
#define INNER_HEAP_SIZE 0x100000
#define INNER_HEAP_SIZE 0x400000
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
char nx_inner_heap[INNER_HEAP_SIZE];
@@ -111,17 +111,12 @@ int main(int argc, char **argv)
/* TODO: Panic. */
}
TmaUsbComms::Initialize();
TmaPacket *packet = new TmaPacket();
usbDsWaitReady(U64_MAX);
packet->Write<u64>(0xCAFEBABEDEADCAFEUL);
packet->Write<u64>(0xCCCCCCCCCCCCCCCCUL);
TmaUsbComms::SendPacket(packet);
packet->ClearOffset();
TmaUsbConnection::InitializeComms();
auto conn = new TmaUsbConnection();
conn->Initialize();
while (true) {
if (TmaUsbComms::ReceivePacket(packet) == TmaConnResult::Success) {
TmaUsbComms::SendPacket(packet);
}
svcSleepThread(10000000UL);
}

View File

@@ -258,12 +258,12 @@ TmaConnResult TmaUsbComms::Initialize() {
}
/* Start the state change thread. */
/*if (R_SUCCEEDED(rc)) {
if (R_SUCCEEDED(rc)) {
rc = g_state_change_thread.Initialize(&TmaUsbComms::UsbStateChangeThreadFunc, nullptr, 0x4000, 38);
if (R_SUCCEEDED(rc)) {
rc = g_state_change_thread.Start();
}
}*/
}
/* Enable USB communication. */
if (R_SUCCEEDED(rc)) {
@@ -277,10 +277,6 @@ TmaConnResult TmaUsbComms::Initialize() {
if (R_FAILED(rc)) {
/* TODO: Should I not abort here? */
std::abort();
// /* Cleanup, just in case. */
// TmaUsbComms::Finalize();
// res = TmaConnResult::Failure;
}
g_initialized = true;
@@ -306,6 +302,10 @@ TmaConnResult TmaUsbComms::Finalize() {
usbDsExit();
}
g_state_change_callback = nullptr;
g_interface = nullptr;
g_endpoint_in = nullptr;
g_endpoint_out = nullptr;
g_initialized = false;
return R_SUCCEEDED(rc) ? TmaConnResult::Success : TmaConnResult::ConnectionFailure;
@@ -458,3 +458,27 @@ TmaConnResult TmaUsbComms::SendPacket(TmaPacket *packet) {
return res;
}
void TmaUsbComms::UsbStateChangeThreadFunc(void *arg) {
u32 state;
g_state_change_manager = new WaitableManager(1);
auto state_change_event = LoadReadOnlySystemEvent(usbDsGetStateChangeEvent()->revent, [&](u64 timeout) {
if (R_SUCCEEDED(usbDsGetState(&state))) {
if (g_state_change_callback != nullptr) {
g_state_change_callback(g_state_change_arg, state);
}
}
return 0;
}, true);
g_state_change_manager->AddWaitable(state_change_event);
g_state_change_manager->Process();
/* If we get here, we're exiting. */
state_change_event->r_h = 0;
delete g_state_change_manager;
g_state_change_manager = nullptr;
svcExitThread();
}

View File

@@ -14,6 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>