mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-23 04:41:12 +00:00
tma: Add target initialization/power management logic
This commit is contained in:
parent
bf7dc84893
commit
37e5a8544b
5 changed files with 344 additions and 40 deletions
|
@ -23,7 +23,7 @@
|
||||||
#include <atmosphere.h>
|
#include <atmosphere.h>
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
#include "tma_conn_usb_connection.hpp"
|
#include "tma_target.hpp"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
extern u32 __start__;
|
extern u32 __start__;
|
||||||
|
@ -74,52 +74,18 @@ void __appExit(void) {
|
||||||
smExit();
|
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)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
consoleDebugInit(debugDevice_SVC);
|
consoleDebugInit(debugDevice_SVC);
|
||||||
Thread pm_thread = {0};
|
|
||||||
if (R_FAILED(threadCreate(&pm_thread, &PmThread, NULL, 0x4000, 0x15, 0))) {
|
/* This will initialize the target. */
|
||||||
/* TODO: Panic. */
|
TmaTarget::Initialize();
|
||||||
}
|
|
||||||
if (R_FAILED(threadStart(&pm_thread))) {
|
|
||||||
/* TODO: Panic. */
|
|
||||||
}
|
|
||||||
|
|
||||||
TmaUsbConnection::InitializeComms();
|
|
||||||
auto conn = new TmaUsbConnection();
|
|
||||||
conn->Initialize();
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
svcSleepThread(10000000UL);
|
svcSleepThread(10000000UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TmaTarget::Finalize();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
69
stratosphere/tma/source/tma_power_manager.cpp
Normal file
69
stratosphere/tma/source/tma_power_manager.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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 */
|
||||||
|
}
|
25
stratosphere/tma/source/tma_power_manager.hpp
Normal file
25
stratosphere/tma/source/tma_power_manager.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
class TmaPowerManager {
|
||||||
|
public:
|
||||||
|
static void Initialize(void (*callback)(PscPmState, u32));
|
||||||
|
static void Finalize();
|
||||||
|
};
|
219
stratosphere/tma/source/tma_target.cpp
Normal file
219
stratosphere/tma/source/tma_target.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
#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<HosMutex> 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... */
|
||||||
|
}
|
25
stratosphere/tma/source/tma_target.hpp
Normal file
25
stratosphere/tma/source/tma_target.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
class TmaTarget {
|
||||||
|
public:
|
||||||
|
static void Initialize();
|
||||||
|
static void Finalize();
|
||||||
|
};
|
Loading…
Reference in a new issue