diff --git a/stratosphere/tma/source/tma_main.cpp b/stratosphere/tma/source/tma_main.cpp index d1b179d4c..fb1491cc3 100644 --- a/stratosphere/tma/source/tma_main.cpp +++ b/stratosphere/tma/source/tma_main.cpp @@ -23,7 +23,7 @@ #include #include -#include "tma_conn_usb_connection.hpp" +#include "tma_target.hpp" extern "C" { extern u32 __start__; @@ -74,52 +74,18 @@ void __appExit(void) { smExit(); } -void PmThread(void *arg) { - /* Setup psc module. */ - Result rc; - PscPmModule tma_module = {0}; - if (R_FAILED((rc = pscGetPmModule(&tma_module, 0x1E, nullptr, 0, true)))) { - fatalSimple(rc); - } - - /* For now, just do what dummy tma does -- loop forever, acknowledging everything. */ - while (true) { - if (R_FAILED((rc = eventWait(&tma_module.event, U64_MAX)))) { - fatalSimple(rc); - } - - PscPmState state; - u32 flags; - if (R_FAILED((rc = pscPmModuleGetRequest(&tma_module, &state, &flags)))) { - fatalSimple(rc); - } - - - if (R_FAILED((rc = pscPmModuleAcknowledge(&tma_module, state)))) { - fatalSimple(rc); - } - } -} - int main(int argc, char **argv) { consoleDebugInit(debugDevice_SVC); - Thread pm_thread = {0}; - if (R_FAILED(threadCreate(&pm_thread, &PmThread, NULL, 0x4000, 0x15, 0))) { - /* TODO: Panic. */ - } - if (R_FAILED(threadStart(&pm_thread))) { - /* TODO: Panic. */ - } - - TmaUsbConnection::InitializeComms(); - auto conn = new TmaUsbConnection(); - conn->Initialize(); + + /* This will initialize the target. */ + TmaTarget::Initialize(); while (true) { svcSleepThread(10000000UL); } - + + TmaTarget::Finalize(); return 0; } diff --git a/stratosphere/tma/source/tma_power_manager.cpp b/stratosphere/tma/source/tma_power_manager.cpp new file mode 100644 index 000000000..f7c04a475 --- /dev/null +++ b/stratosphere/tma/source/tma_power_manager.cpp @@ -0,0 +1,69 @@ +/* + * 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_power_manager.hpp" + +static constexpr u16 PscPmModuleId_Usb = 0x04; +static constexpr u16 PscPmModuleId_Pcie = 0x13; +static constexpr u16 PscPmModuleId_Tma = 0x1E; + +static const u16 g_tma_pm_dependencies[] = { + PscPmModuleId_Pcie, + PscPmModuleId_Usb, +}; + +static void (*g_pm_callback)(PscPmState, u32) = nullptr; +static HosThread g_pm_thread; + +static void PowerManagerThread(void *arg) { + /* Setup psc module. */ + Result rc; + PscPmModule tma_module = {0}; + if (R_FAILED((rc = pscGetPmModule(&tma_module, PscPmModuleId_Tma, g_tma_pm_dependencies, sizeof(g_tma_pm_dependencies)/sizeof(u16), true)))) { + fatalSimple(rc); + } + + /* For now, just do what dummy tma does -- loop forever, acknowledging everything. */ + while (true) { + if (R_FAILED((rc = eventWait(&tma_module.event, U64_MAX)))) { + fatalSimple(rc); + } + + PscPmState state; + u32 flags; + if (R_FAILED((rc = pscPmModuleGetRequest(&tma_module, &state, &flags)))) { + fatalSimple(rc); + } + + g_pm_callback(state, flags); + + if (R_FAILED((rc = pscPmModuleAcknowledge(&tma_module, state)))) { + fatalSimple(rc); + } + } +} + +void TmaPowerManager::Initialize(void (*callback)(PscPmState, u32)) { + g_pm_callback = callback; + g_pm_thread.Initialize(PowerManagerThread, nullptr, 0x4000, 0x15); + g_pm_thread.Start(); +} + +void TmaPowerManager::Finalize() { + /* TODO */ +} \ No newline at end of file diff --git a/stratosphere/tma/source/tma_power_manager.hpp b/stratosphere/tma/source/tma_power_manager.hpp new file mode 100644 index 000000000..6ca0e4ad8 --- /dev/null +++ b/stratosphere/tma/source/tma_power_manager.hpp @@ -0,0 +1,25 @@ +/* + * 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 + +class TmaPowerManager { + public: + static void Initialize(void (*callback)(PscPmState, u32)); + static void Finalize(); +}; diff --git a/stratosphere/tma/source/tma_target.cpp b/stratosphere/tma/source/tma_target.cpp new file mode 100644 index 000000000..234345da1 --- /dev/null +++ b/stratosphere/tma/source/tma_target.cpp @@ -0,0 +1,219 @@ +/* + * 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_conn_connection.hpp" +#include "tma_conn_usb_connection.hpp" + +#include "tma_service_manager.hpp" +#include "tma_power_manager.hpp" + +#include "tma_target.hpp" + +struct TmaTargetConfig { + char configuration_id1[0x80]; + char serial_number[0x80]; +}; + +static TmaConnection *g_active_connection = nullptr; +static TmaServiceManager *g_service_manager = nullptr; +static HosMutex g_connection_event_mutex; +static bool g_has_woken_up = false; +static bool g_signal_on_disconnect = false; + +static TmaUsbConnection *g_usb_connection = nullptr; + +static TmaTargetConfig g_target_config = { + "Unknown", + "SerialNumber", +}; + +static void RefreshTargetConfig() { + setsysInitialize(); + + /* TODO: setsysGetConfigurationId1(&g_target_config.configuration_id1); */ + + g_target_config.serial_number[0] = 0; + setsysGetSerialNumber(g_target_config.serial_number); + + setsysExit(); +} + +static void InitializeServices() { + g_service_manager->Initialize(); +} + +static void FinalizeServices() { + g_service_manager->Finalize(); +} + +static void SetActiveConnection(TmaConnection *connection) { + if (g_active_connection != connection) { + if (g_active_connection != nullptr) { + FinalizeServices(); + g_service_manager->SetConnection(nullptr); + g_active_connection->Disconnect(); + g_active_connection = nullptr; + } + + if (connection != nullptr) { + g_active_connection = connection; + InitializeServices(); + g_service_manager->SetConnection(g_active_connection); + g_active_connection->SetServiceManager(g_service_manager); + } + } +} + +static void OnConnectionEvent(void *arg, ConnectionEvent evt) { + std::scoped_lock lk(g_connection_event_mutex); + + switch (evt) { + case ConnectionEvent::Connected: + { + bool has_active_connection = false; + g_has_woken_up = false; + + if (arg == g_usb_connection) { + SetActiveConnection(g_usb_connection); + has_active_connection = true; + } + + if (has_active_connection) { + /* TODO: Signal connected */ + } + } + break; + case ConnectionEvent::Disconnected: + if (g_signal_on_disconnect) { + /* TODO: Signal disconnected */ + } + break; + default: + std::abort(); + break; + } +} + +static void Wake() { + if (g_service_manager->GetAsleep()) { + g_has_woken_up = true; + + /* N checks what kind of connection to use here. For now, we only use USB. */ + TmaUsbConnection::InitializeComms(); + g_usb_connection = new TmaUsbConnection(); + g_usb_connection->SetConnectionEventCallback(OnConnectionEvent, g_usb_connection); + g_usb_connection->Initialize(); + SetActiveConnection(g_usb_connection); + + g_service_manager->Wake(g_active_connection); + } +} + +static void Sleep() { + if (!g_service_manager->GetAsleep()) { + if (g_active_connection->IsConnected()) { + /* TODO: Send a packet saying we're going to sleep. */ + } + + g_service_manager->Sleep(); + g_service_manager->SetConnection(nullptr); + g_active_connection->Disconnect(); + g_active_connection = nullptr; + g_service_manager->CancelTasks(); + + if (g_usb_connection != nullptr) { + g_usb_connection->Finalize(); + delete g_usb_connection; + g_usb_connection = nullptr; + TmaUsbConnection::FinalizeComms(); + } + } +} + +static void OnPowerManagementEvent(PscPmState state, u32 flags) { + switch (state) { + case PscPmState_Awake: + { + Wake(); + } + break; + case PscPmState_ReadyAwaken: + { + if (g_service_manager->GetAsleep()) { + Wake(); + { + /* Try to restore a connection. */ + bool connected = g_service_manager->GetConnected(); + + /* N uses a seven-second timeout, here. */ + TimeoutHelper timeout_helper(7000000000ULL); + while (!connected && !timeout_helper.TimedOut()) { + connected = g_service_manager->GetConnected(); + if (!connected) { + /* Sleep for 1ms. */ + svcSleepThread(1000000ULL); + } + } + if (!connected) { + /* TODO: Signal disconnected */ + } + } + } + } + break; + case PscPmState_ReadySleep: + { + Sleep(); + } + break; + default: + /* Don't handle ReadySleepCritical/ReadyAwakenCritical/ReadyShutdown */ + break; + } +} + +void TmaTarget::Initialize() { + /* Get current thread priority. */ + u32 cur_prio; + if (R_FAILED(svcGetThreadPriority(&cur_prio, CUR_THREAD_HANDLE))) { + std::abort(); + } + + g_active_connection = nullptr; + g_service_manager = new TmaServiceManager(); + + RefreshTargetConfig(); + + /* N checks what kind of connection to use here. For now, we only use USB. */ + TmaUsbConnection::InitializeComms(); + g_usb_connection = new TmaUsbConnection(); + g_usb_connection->SetConnectionEventCallback(OnConnectionEvent, g_usb_connection); + g_usb_connection->Initialize(); + SetActiveConnection(g_usb_connection); + + /* TODO: Initialize connection events */ + + /* TODO: Initialize IPC services */ + + TmaPowerManager::Initialize(OnPowerManagementEvent); +} + +void TmaTarget::Finalize() { + /* TODO: Is implementing this actually worthwhile? It will never be called in practice... */ +} diff --git a/stratosphere/tma/source/tma_target.hpp b/stratosphere/tma/source/tma_target.hpp new file mode 100644 index 000000000..73b81e857 --- /dev/null +++ b/stratosphere/tma/source/tma_target.hpp @@ -0,0 +1,25 @@ +/* + * 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 + +class TmaTarget { + public: + static void Initialize(); + static void Finalize(); +};