sys-clk-OC
This commit is contained in:
524
Source/sys-clk-OC/sysmodule/src/clock_manager.cpp
Normal file
524
Source/sys-clk-OC/sysmodule/src/clock_manager.cpp
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define FORCE_ALL_HANDHELD_MODES_TO_USE_DOCK_CLOCK
|
||||
#include <nxExt.h>
|
||||
#include "errors.h"
|
||||
#include "clock_manager.h"
|
||||
#include "file_utils.h"
|
||||
#include "clocks.h"
|
||||
#include "process_management.h"
|
||||
#include <cstring>
|
||||
|
||||
ClockManager* ClockManager::instance = NULL;
|
||||
|
||||
ClockManager* ClockManager::GetInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ClockManager::Exit()
|
||||
{
|
||||
if(instance)
|
||||
{
|
||||
delete instance;
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::Initialize()
|
||||
{
|
||||
if(!instance)
|
||||
{
|
||||
instance = new ClockManager();
|
||||
}
|
||||
}
|
||||
|
||||
ClockManager::ClockManager()
|
||||
{
|
||||
this->config = Config::CreateDefault();
|
||||
this->context = new SysClkContext;
|
||||
this->context->applicationId = 0;
|
||||
this->context->profile = SysClkProfile_Handheld;
|
||||
this->context->enabled = false;
|
||||
for(unsigned int i = 0; i < SysClkModule_EnumMax; i++)
|
||||
{
|
||||
this->context->freqs[i] = 0;
|
||||
this->context->overrideFreqs[i] = 0;
|
||||
}
|
||||
this->running = false;
|
||||
this->lastTempLogNs = 0;
|
||||
this->lastCsvWriteNs = 0;
|
||||
}
|
||||
|
||||
ClockManager::~ClockManager()
|
||||
{
|
||||
delete this->config;
|
||||
delete this->context;
|
||||
}
|
||||
|
||||
bool ClockManager::IsCpuBoostMode()
|
||||
{
|
||||
std::uint32_t confId = 0;
|
||||
Result rc = 0;
|
||||
rc = apmExtGetCurrentPerformanceConfiguration(&confId);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
|
||||
if(confId == 0x92220009 || confId == 0x9222000A)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
SysClkProfile ClockManager::ReverseNXProfile(bool ForceDock)
|
||||
{
|
||||
RealProfile = Clocks::GetCurrentProfile();
|
||||
switch(RealProfile)
|
||||
{
|
||||
case SysClkProfile_HandheldChargingOfficial:
|
||||
#ifdef FORCE_ALL_HANDHELD_MODES_TO_USE_DOCK_CLOCK
|
||||
case SysClkProfile_HandheldChargingUSB:
|
||||
case SysClkProfile_HandheldCharging:
|
||||
case SysClkProfile_Handheld:
|
||||
#endif
|
||||
if (ForceDock)
|
||||
return SysClkProfile_Docked;
|
||||
else
|
||||
return RealProfile;
|
||||
case SysClkProfile_Docked:
|
||||
if (ForceDock)
|
||||
return SysClkProfile_Docked;
|
||||
else
|
||||
return FileUtils::IsDownclockDockEnabled() ? SysClkProfile_HandheldChargingOfficial : SysClkProfile_Docked;
|
||||
default:
|
||||
return RealProfile;
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::checkReverseNXTool()
|
||||
{
|
||||
char ReverseNXToolAsm[] = "_ZN2nn2oe18GetPerformanceModeEv.asm64"; // Checking one asm64 file is enough
|
||||
char ReverseNXToolAsmPath[128];
|
||||
uint8_t flag = 0;
|
||||
snprintf(ReverseNXToolAsmPath, sizeof ReverseNXToolAsmPath, "/SaltySD/patches/%s", ReverseNXToolAsm);
|
||||
|
||||
FILE *readReverseNXToolAsm;
|
||||
readReverseNXToolAsm = fopen(ReverseNXToolAsmPath, "rb");
|
||||
|
||||
// Enforce mode globally: Enabled
|
||||
if(readReverseNXToolAsm != NULL)
|
||||
{
|
||||
checkReverseNXToolAsm(readReverseNXToolAsm, &flag);
|
||||
switch(flag)
|
||||
{
|
||||
case 1:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Enforce Handheld globally");
|
||||
this->context->profile = ReverseNXProfile(false);
|
||||
isDockedReverseNX = false;
|
||||
isEnabledReverseNX = true;
|
||||
isEnabledReverseNXTool = true;
|
||||
break;
|
||||
case 2:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Enforce Docked globally");
|
||||
this->context->profile = ReverseNXProfile(true);
|
||||
isDockedReverseNX = true;
|
||||
isEnabledReverseNX = true;
|
||||
isEnabledReverseNXTool = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(ReverseNXToolAsmPath, sizeof ReverseNXToolAsmPath, "/SaltySD/patches/%016lX/%s", this->context->applicationId, ReverseNXToolAsm);
|
||||
readReverseNXToolAsm = fopen(ReverseNXToolAsmPath, "rb");
|
||||
// Found game-specific setting
|
||||
if(readReverseNXToolAsm != NULL)
|
||||
{
|
||||
checkReverseNXToolAsm(readReverseNXToolAsm, &flag);
|
||||
switch(flag)
|
||||
{
|
||||
case 1:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Force Handheld in %016lX", this->context->applicationId);
|
||||
this->context->profile = ReverseNXProfile(false);
|
||||
isDockedReverseNX = false;
|
||||
isEnabledReverseNX = true;
|
||||
isEnabledReverseNXTool = true;
|
||||
break;
|
||||
case 2:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Force Docked in %016lX", this->context->applicationId);
|
||||
this->context->profile = ReverseNXProfile(true);
|
||||
isDockedReverseNX = true;
|
||||
isEnabledReverseNX = true;
|
||||
isEnabledReverseNXTool = true;
|
||||
break;
|
||||
default:
|
||||
isEnabledReverseNXTool = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ClockManager::GameStartBoost()
|
||||
{
|
||||
if (tickStartBoost && this->GetConfig()->Enabled())
|
||||
{
|
||||
if (Clocks::GetCurrentHz(SysClkModule_CPU) != MAX_CPU)
|
||||
{
|
||||
Clocks::SetHz(SysClkModule_CPU, MAX_CPU);
|
||||
this->context->freqs[SysClkModule_CPU] = MAX_CPU;
|
||||
}
|
||||
|
||||
std::uint64_t applicationId = ProcessManagement::GetCurrentApplicationId();
|
||||
// If user exit the game
|
||||
if (applicationId != this->context->applicationId)
|
||||
{
|
||||
tickStartBoost = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tickStartBoost == 1)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] Boost done, reset to stock");
|
||||
Clocks::ResetToStock();
|
||||
}
|
||||
tickStartBoost--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClockManager::SetRunning(bool running)
|
||||
{
|
||||
this->running = running;
|
||||
}
|
||||
|
||||
bool ClockManager::Running()
|
||||
{
|
||||
return this->running;
|
||||
}
|
||||
|
||||
void ClockManager::Tick()
|
||||
{
|
||||
std::scoped_lock lock{this->contextMutex};
|
||||
|
||||
if(!GameStartBoost())
|
||||
{
|
||||
bool cpuBoost = FileUtils::IsBoostEnabled() ? IsCpuBoostMode() : false;
|
||||
if (this->RefreshContext() || this->config->Refresh())
|
||||
{
|
||||
std::uint32_t hz = 0;
|
||||
std::uint32_t hzForceOverride = 0;
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
hz = this->context->overrideFreqs[module];
|
||||
|
||||
if(!hz)
|
||||
{
|
||||
hz = this->config->GetAutoClockHz(this->context->applicationId, (SysClkModule)module, this->context->profile);
|
||||
hzForceOverride = this->config->GetAutoClockHz(0xA111111111111111, (SysClkModule)module, this->context->profile);
|
||||
if (!hz && hzForceOverride)
|
||||
hz = hzForceOverride;
|
||||
|
||||
if(isEnabledReverseNX && !hz)
|
||||
{
|
||||
switch(module)
|
||||
{
|
||||
case SysClkModule_CPU:
|
||||
hz = 1020'000'000;
|
||||
break;
|
||||
case SysClkModule_GPU:
|
||||
if (!isDockedReverseNX && ((FileUtils::IsDownclockDockEnabled() && RealProfile == SysClkProfile_Docked)
|
||||
|| RealProfile != SysClkProfile_Docked))
|
||||
hz = 460'800'000;
|
||||
else
|
||||
hz = 768'000'000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (hz)
|
||||
{
|
||||
hz = Clocks::GetNearestHz((SysClkModule)module, isEnabledReverseNX ? RealProfile : this->context->profile, hz);
|
||||
|
||||
if (hz != this->context->freqs[module] && this->context->enabled)
|
||||
{
|
||||
if (cpuBoost)
|
||||
{
|
||||
if (module == SysClkModule_CPU && hz < MAX_CPU)
|
||||
{
|
||||
hz = MAX_CPU;
|
||||
FileUtils::LogLine("[mgr] CpuBoostMode detected, bump CPU to max");
|
||||
}
|
||||
}
|
||||
FileUtils::LogLine("[mgr] %s clock set : %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
Clocks::SetHz((SysClkModule)module, hz);
|
||||
this->context->freqs[module] = hz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (FileUtils::IsBoostEnabled())
|
||||
{
|
||||
// If user doesn't set any freq but with sys-clk enabled, then boost CPU in CpuBoostMode
|
||||
if(cpuBoost && this->GetConfig()->Enabled())
|
||||
{
|
||||
if(this->context->freqs[SysClkModule_CPU] != MAX_CPU)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] CpuBoostMode detected, bump CPU to max");
|
||||
Clocks::SetHz(SysClkModule_CPU, MAX_CPU);
|
||||
this->context->freqs[SysClkModule_CPU] = MAX_CPU;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::WaitForNextTick()
|
||||
{
|
||||
svcSleepThread(this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs) * 1000000ULL);
|
||||
}
|
||||
|
||||
void ClockManager::checkReverseNXToolAsm(FILE* readFile, uint8_t* flag)
|
||||
{
|
||||
// Copied from ReverseNXTool
|
||||
uint8_t Docked[0x10] = {0xE0, 0x03, 0x00, 0x32, 0xC0, 0x03, 0x5F, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
uint8_t Handheld[0x10] = {0x00, 0x00, 0xA0, 0x52, 0xC0, 0x03, 0x5F, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
uint8_t filebuffer[0x10] = {0};
|
||||
uint8_t cmpresult = 0;
|
||||
fread(&filebuffer, 1, 16, readFile);
|
||||
cmpresult = memcmp(filebuffer, Docked, sizeof(Docked));
|
||||
if (cmpresult != 0)
|
||||
{
|
||||
cmpresult = memcmp(filebuffer, Handheld, sizeof(Handheld));
|
||||
if (cmpresult != 0)
|
||||
*flag = 0; // Set to default
|
||||
else
|
||||
*flag = 1; // Handheld
|
||||
}
|
||||
else
|
||||
*flag = 2; // Docked
|
||||
|
||||
fclose(readFile);
|
||||
}
|
||||
|
||||
void ClockManager::checkReverseNXRT(bool recheckReverseNX, uint8_t* flag)
|
||||
{
|
||||
FILE* readReverseNXRTConf = fopen(FILE_REVERSENX_RT_CONF_PATH, "rb");
|
||||
if (readReverseNXRTConf != NULL)
|
||||
{
|
||||
uint8_t ReverseNXRTConfArr[9];
|
||||
fread(ReverseNXRTConfArr, 9, 1, readReverseNXRTConf);
|
||||
fclose(readReverseNXRTConf);
|
||||
remove(FILE_REVERSENX_RT_CONF_PATH);
|
||||
|
||||
uint8_t currentTid[8];
|
||||
for(int i = 0; i < 8; i++)
|
||||
currentTid[i] = this->context->applicationId >> 8*(7-i);
|
||||
|
||||
uint8_t cmpresult = memcmp(currentTid, ReverseNXRTConfArr, sizeof(currentTid));
|
||||
|
||||
if (cmpresult == 0)
|
||||
*flag = ReverseNXRTConfArr[8]; // 1: Handheld, 2: Docked, 3: Reset
|
||||
else
|
||||
*flag = 0; // 0: Not applicable
|
||||
}
|
||||
else if (recheckReverseNX)
|
||||
*flag = prevReverseNXRT; // Use previous state when profile changes
|
||||
else
|
||||
*flag = 0;
|
||||
}
|
||||
|
||||
bool ClockManager::RefreshContext()
|
||||
{
|
||||
bool hasChanged = false;
|
||||
|
||||
bool enabled = this->GetConfig()->Enabled();
|
||||
if(enabled != this->context->enabled)
|
||||
{
|
||||
this->context->enabled = enabled;
|
||||
FileUtils::LogLine("[mgr] " TARGET " status: %s", enabled ? "enabled" : "disabled");
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
std::uint64_t applicationId = ProcessManagement::GetCurrentApplicationId();
|
||||
if (applicationId != this->context->applicationId)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] TitleID change: %016lX", applicationId);
|
||||
prevReverseNXRT = 0; // Reset ReverseNX-RT previous state when Title ID changes
|
||||
this->context->applicationId = applicationId;
|
||||
hasChanged = true;
|
||||
|
||||
if (FileUtils::IsReverseNXEnabled() || recheckReverseNX)
|
||||
{
|
||||
// A new game starts or the real profile changes, then we need to check if ReverseNXTool patches are applied
|
||||
isEnabledReverseNX = false;
|
||||
|
||||
// Check if ReverseNXTool patches are applied
|
||||
if (applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID)
|
||||
this->checkReverseNXTool();
|
||||
}
|
||||
|
||||
if (FileUtils::IsBoostStartEnabled() && this->context->applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID)
|
||||
{
|
||||
// If a game starts and overrides for CPU are not enabled, then set MAX_CPU for 10 sec
|
||||
std::uint32_t overcpu = this->context->overrideFreqs[SysClkModule_CPU];
|
||||
if (!overcpu)
|
||||
{
|
||||
tickStartBoost = (std::uint32_t)( 10'000 / this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs) ) + 1;
|
||||
FileUtils::LogLine("[mgr] A game starts, bump CPU to max for 10 sec");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!tickCheckReverseNXRT || recheckReverseNX)
|
||||
{
|
||||
uint8_t flag = 0;
|
||||
checkReverseNXRT(recheckReverseNX, &flag);
|
||||
|
||||
switch(flag)
|
||||
{
|
||||
case 1:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-RT detected: Enforce Handheld Mode");
|
||||
this->context->profile = ReverseNXProfile(false);
|
||||
prevReverseNXRT = flag;
|
||||
isEnabledReverseNX = true;
|
||||
isDockedReverseNX = false;
|
||||
hasChanged = true;
|
||||
break;
|
||||
case 2:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-RT detected: Enforce Docked Mode");
|
||||
this->context->profile = ReverseNXProfile(true);
|
||||
prevReverseNXRT = flag;
|
||||
isEnabledReverseNX = true;
|
||||
isDockedReverseNX = true;
|
||||
hasChanged = true;
|
||||
break;
|
||||
case 3:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-RT disabled: Reset to System-controlled Mode and recheck ReverseNX-Tool");
|
||||
RealProfile = Clocks::GetCurrentProfile();
|
||||
this->context->profile = RealProfile;
|
||||
prevReverseNXRT = 0;
|
||||
isEnabledReverseNX = false;
|
||||
isDockedReverseNX = false;
|
||||
hasChanged = true;
|
||||
if (this->context->applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID)
|
||||
this->checkReverseNXTool();
|
||||
break;
|
||||
case 0:
|
||||
if (recheckReverseNX && isEnabledReverseNXTool && this->context->applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID)
|
||||
this->checkReverseNXTool();
|
||||
break;
|
||||
}
|
||||
// Check once per sec
|
||||
tickCheckReverseNXRT = (std::uint32_t)( 1'000 / this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs) ) + 1;
|
||||
}
|
||||
tickCheckReverseNXRT--;
|
||||
|
||||
if (recheckReverseNX)
|
||||
recheckReverseNX = false;
|
||||
|
||||
SysClkProfile profile = Clocks::GetCurrentProfile();
|
||||
if (profile != this->context->profile && !isEnabledReverseNX)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] Profile change: %s", Clocks::GetProfileName(profile, true));
|
||||
this->context->profile = profile;
|
||||
hasChanged = true;
|
||||
}
|
||||
if (profile != RealProfile && isEnabledReverseNX)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] Profile change: %s, recheck ReverseNX", Clocks::GetProfileName(profile, true));
|
||||
this->context->profile = profile;
|
||||
RealProfile = profile;
|
||||
hasChanged = true;
|
||||
recheckReverseNX = true;
|
||||
}
|
||||
|
||||
// restore clocks to stock values on app or profile change
|
||||
if(hasChanged)
|
||||
{
|
||||
if (profile == SysClkProfile_Handheld)
|
||||
MAX_CPU = 1963'500'000;
|
||||
else
|
||||
MAX_CPU = 2295'000'000;
|
||||
Clocks::ResetToStock();
|
||||
}
|
||||
|
||||
std::uint32_t hz = 0;
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
hz = Clocks::GetCurrentHz((SysClkModule)module);
|
||||
uint32_t cur_mhz = hz/1000'000;
|
||||
uint32_t be4_mhz = this->context->freqs[module]/1000'000;
|
||||
if (hz != 0 && cur_mhz != be4_mhz)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s clock change: %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
this->context->freqs[module] = hz;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
hz = this->GetConfig()->GetOverrideHz((SysClkModule)module);
|
||||
if (hz != this->context->overrideFreqs[module])
|
||||
{
|
||||
if(hz)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s override change: %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s override disabled", Clocks::GetModuleName((SysClkModule)module, true));
|
||||
}
|
||||
this->context->overrideFreqs[module] = hz;
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// temperatures do not and should not force a refresh, hasChanged untouched
|
||||
std::uint32_t millis = 0;
|
||||
std::uint64_t ns = armTicksToNs(armGetSystemTick());
|
||||
std::uint64_t tempLogInterval = this->GetConfig()->GetConfigValue(SysClkConfigValue_TempLogIntervalMs) * 1000000ULL;
|
||||
bool shouldLogTemp = tempLogInterval && ((ns - this->lastTempLogNs) > tempLogInterval);
|
||||
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++)
|
||||
{
|
||||
millis = Clocks::GetTemperatureMilli((SysClkThermalSensor)sensor);
|
||||
if(shouldLogTemp)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s temp: %u.%u °C", Clocks::GetThermalSensorName((SysClkThermalSensor)sensor, true), millis/1000, (millis - millis/1000*1000) / 100);
|
||||
}
|
||||
this->context->temps[sensor] = millis;
|
||||
}
|
||||
|
||||
if(shouldLogTemp)
|
||||
{
|
||||
this->lastTempLogNs = ns;
|
||||
}
|
||||
|
||||
std::uint64_t csvWriteInterval = this->GetConfig()->GetConfigValue(SysClkConfigValue_CsvWriteIntervalMs) * 1000000ULL;
|
||||
|
||||
if(csvWriteInterval && ((ns - this->lastCsvWriteNs) > csvWriteInterval))
|
||||
{
|
||||
FileUtils::WriteContextToCsv(this->context);
|
||||
this->lastCsvWriteNs = ns;
|
||||
}
|
||||
|
||||
return hasChanged;
|
||||
}
|
||||
|
||||
SysClkContext ClockManager::GetCurrentContext()
|
||||
{
|
||||
std::scoped_lock lock{this->contextMutex};
|
||||
return *this->context;
|
||||
}
|
||||
|
||||
Config* ClockManager::GetConfig()
|
||||
{
|
||||
return this->config;
|
||||
}
|
||||
65
Source/sys-clk-OC/sysmodule/src/clock_manager.h
Normal file
65
Source/sys-clk-OC/sysmodule/src/clock_manager.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <sysclk.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "clocks.h"
|
||||
#include <nxExt/cpp/lockable_mutex.h>
|
||||
|
||||
class ClockManager
|
||||
{
|
||||
public:
|
||||
std::uint32_t MAX_CPU = 1963500000;
|
||||
|
||||
static ClockManager* GetInstance();
|
||||
static void Initialize();
|
||||
static void Exit();
|
||||
|
||||
bool recheckReverseNX = false;
|
||||
bool isEnabledReverseNX = false;
|
||||
bool isEnabledReverseNXTool = false;
|
||||
bool isDockedReverseNX = false;
|
||||
std::uint16_t tickCheckReverseNXRT = 0;
|
||||
std::uint16_t tickStartBoost = 0;
|
||||
char prevReverseNXRT = 0;
|
||||
SysClkProfile RealProfile;
|
||||
|
||||
bool IsCpuBoostMode();
|
||||
SysClkProfile ReverseNXProfile(bool);
|
||||
void checkReverseNXTool();
|
||||
bool GameStartBoost();
|
||||
|
||||
void SetRunning(bool running);
|
||||
bool Running();
|
||||
void Tick();
|
||||
void WaitForNextTick();
|
||||
void checkReverseNXToolAsm(FILE*, uint8_t*);
|
||||
void checkReverseNXRT(bool, uint8_t*);
|
||||
SysClkContext GetCurrentContext();
|
||||
Config* GetConfig();
|
||||
|
||||
protected:
|
||||
ClockManager();
|
||||
virtual ~ClockManager();
|
||||
|
||||
bool RefreshContext();
|
||||
|
||||
static ClockManager *instance;
|
||||
std::atomic_bool running;
|
||||
LockableMutex contextMutex;
|
||||
Config *config;
|
||||
SysClkContext *context;
|
||||
std::uint64_t lastTempLogNs;
|
||||
std::uint64_t lastCsvWriteNs;
|
||||
};
|
||||
454
Source/sys-clk-OC/sysmodule/src/clocks.cpp
Normal file
454
Source/sys-clk-OC/sysmodule/src/clocks.cpp
Normal file
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <nxExt.h>
|
||||
#include "clocks.h"
|
||||
#include "errors.h"
|
||||
|
||||
bool Clocks::isMariko = false;
|
||||
|
||||
void Clocks::GetList(SysClkModule module, std::uint32_t **outClocks)
|
||||
{
|
||||
switch(module)
|
||||
{
|
||||
case SysClkModule_CPU:
|
||||
*outClocks = sysclk_g_freq_table_cpu_hz;
|
||||
break;
|
||||
case SysClkModule_GPU:
|
||||
*outClocks = sysclk_g_freq_table_gpu_hz;
|
||||
break;
|
||||
case SysClkModule_MEM:
|
||||
*outClocks = sysclk_g_freq_table_mem_hz;
|
||||
break;
|
||||
default:
|
||||
*outClocks = NULL;
|
||||
ERROR_THROW("No such PcvModule: %u", module);
|
||||
}
|
||||
}
|
||||
|
||||
void Clocks::Initialize()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
if(hosversionAtLeast(8,0,0))
|
||||
{
|
||||
rc = clkrstInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pcvInitialize");
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = pcvInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pcvInitialize");
|
||||
}
|
||||
|
||||
rc = apmExtInitialize();
|
||||
ASSERT_RESULT_OK(rc, "apmExtInitialize");
|
||||
|
||||
rc = psmInitialize();
|
||||
ASSERT_RESULT_OK(rc, "psmInitialize");
|
||||
|
||||
rc = tsInitialize();
|
||||
ASSERT_RESULT_OK(rc, "tsInitialize");
|
||||
|
||||
if(hosversionAtLeast(5,0,0))
|
||||
{
|
||||
rc = tcInitialize();
|
||||
ASSERT_RESULT_OK(rc, "tcInitialize");
|
||||
}
|
||||
|
||||
// Check if it's Mariko
|
||||
u64 hardware_type = 0;
|
||||
splInitialize();
|
||||
splGetConfig(SplConfigItem_HardwareType, &hardware_type);
|
||||
splExit();
|
||||
|
||||
switch(hardware_type) {
|
||||
case 0: //Icosa
|
||||
case 1: //Copper
|
||||
break;
|
||||
case 2: //Hoag
|
||||
case 3: //Iowa
|
||||
case 4: //Calcio
|
||||
case 5: //Aula
|
||||
isMariko = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Clocks::Exit()
|
||||
{
|
||||
if(hosversionAtLeast(8,0,0))
|
||||
{
|
||||
pcvExit();
|
||||
}
|
||||
else
|
||||
{
|
||||
clkrstExit();
|
||||
}
|
||||
|
||||
apmExtExit();
|
||||
psmExit();
|
||||
tsExit();
|
||||
|
||||
if(hosversionAtLeast(5,0,0))
|
||||
{
|
||||
tcExit();
|
||||
}
|
||||
}
|
||||
|
||||
const char* Clocks::GetModuleName(SysClkModule module, bool pretty)
|
||||
{
|
||||
const char* result = sysclkFormatModule(module, pretty);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
ERROR_THROW("No such SysClkModule: %u", module);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* Clocks::GetProfileName(SysClkProfile profile, bool pretty)
|
||||
{
|
||||
const char* result = sysclkFormatProfile(profile, pretty);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
ERROR_THROW("No such SysClkProfile: %u", profile);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* Clocks::GetThermalSensorName(SysClkThermalSensor sensor, bool pretty)
|
||||
{
|
||||
const char* result = sysclkFormatThermalSensor(sensor, pretty);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
ERROR_THROW("No such SysClkThermalSensor: %u", sensor);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PcvModule Clocks::GetPcvModule(SysClkModule sysclkModule)
|
||||
{
|
||||
switch(sysclkModule)
|
||||
{
|
||||
case SysClkModule_CPU:
|
||||
return PcvModule_CpuBus;
|
||||
case SysClkModule_GPU:
|
||||
return PcvModule_GPU;
|
||||
case SysClkModule_MEM:
|
||||
return PcvModule_EMC;
|
||||
default:
|
||||
ERROR_THROW("No such SysClkModule: %u", sysclkModule);
|
||||
}
|
||||
|
||||
return (PcvModule)0;
|
||||
}
|
||||
|
||||
PcvModuleId Clocks::GetPcvModuleId(SysClkModule sysclkModule)
|
||||
{
|
||||
PcvModuleId pcvModuleId;
|
||||
Result rc = pcvGetModuleId(&pcvModuleId, GetPcvModule(sysclkModule));
|
||||
ASSERT_RESULT_OK(rc, "pcvGetModuleId");
|
||||
|
||||
return pcvModuleId;
|
||||
}
|
||||
|
||||
void Clocks::ResetToStock()
|
||||
{
|
||||
Result rc = 0;
|
||||
if(hosversionAtLeast(9,0,0))
|
||||
{
|
||||
std::uint32_t confId = 0;
|
||||
rc = apmExtGetCurrentPerformanceConfiguration(&confId);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
|
||||
|
||||
SysClkApmConfiguration* apmConfiguration = NULL;
|
||||
for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++)
|
||||
{
|
||||
if(sysclk_g_apm_configurations[i].id == confId)
|
||||
{
|
||||
apmConfiguration = &sysclk_g_apm_configurations[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!apmConfiguration)
|
||||
{
|
||||
ERROR_THROW("Unknown apm configuration: %x", confId);
|
||||
}
|
||||
|
||||
Clocks::SetHz(SysClkModule_CPU, apmConfiguration->cpu_hz);
|
||||
Clocks::SetHz(SysClkModule_GPU, apmConfiguration->gpu_hz);
|
||||
// We don't need to set MEM freqs any more
|
||||
//Clocks::SetHz(SysClkModule_MEM, apmConfiguration->mem_hz);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::uint32_t mode = 0;
|
||||
rc = apmExtGetPerformanceMode(&mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
|
||||
|
||||
rc = apmExtSysRequestPerformanceMode(mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode");
|
||||
}
|
||||
}
|
||||
|
||||
SysClkProfile Clocks::GetCurrentProfile()
|
||||
{
|
||||
std::uint32_t mode = 0;
|
||||
Result rc = apmExtGetPerformanceMode(&mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
|
||||
|
||||
if(mode)
|
||||
{
|
||||
return SysClkProfile_Docked;
|
||||
}
|
||||
|
||||
PsmChargerType chargerType;
|
||||
|
||||
rc = psmGetChargerType(&chargerType);
|
||||
ASSERT_RESULT_OK(rc, "psmGetChargerType");
|
||||
|
||||
if(chargerType == PsmChargerType_EnoughPower)
|
||||
{
|
||||
return SysClkProfile_HandheldChargingOfficial;
|
||||
}
|
||||
else if(chargerType == PsmChargerType_LowPower || chargerType == PsmChargerType_NotSupported)
|
||||
{
|
||||
return SysClkProfile_HandheldChargingUSB;
|
||||
}
|
||||
|
||||
return SysClkProfile_Handheld;
|
||||
}
|
||||
|
||||
void Clocks::SetHz(SysClkModule module, std::uint32_t hz)
|
||||
{
|
||||
// We don't need to set MEM freqs any more
|
||||
if (module == SysClkModule_MEM)
|
||||
return;
|
||||
|
||||
Result rc = 0;
|
||||
|
||||
if(hosversionAtLeast(8,0,0))
|
||||
{
|
||||
ClkrstSession session = {0};
|
||||
|
||||
rc = clkrstOpenSession(&session, Clocks::GetPcvModuleId(module), 3);
|
||||
ASSERT_RESULT_OK(rc, "clkrstOpenSession");
|
||||
rc = clkrstSetClockRate(&session, hz);
|
||||
ASSERT_RESULT_OK(rc, "clkrstSetClockRate");
|
||||
|
||||
clkrstCloseSession(&session);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = pcvSetClockRate(Clocks::GetPcvModule(module), hz);
|
||||
ASSERT_RESULT_OK(rc, "pcvSetClockRate");
|
||||
}
|
||||
}
|
||||
|
||||
std::uint32_t Clocks::GetCurrentHz(SysClkModule module)
|
||||
{
|
||||
Result rc = 0;
|
||||
std::uint32_t hz = 0;
|
||||
|
||||
if(hosversionAtLeast(8,0,0))
|
||||
{
|
||||
ClkrstSession session = {0};
|
||||
|
||||
rc = clkrstOpenSession(&session, Clocks::GetPcvModuleId(module), 3);
|
||||
ASSERT_RESULT_OK(rc, "clkrstOpenSession");
|
||||
|
||||
rc = clkrstGetClockRate(&session, &hz);
|
||||
ASSERT_RESULT_OK(rc, "clkrstGetClockRate");
|
||||
|
||||
clkrstCloseSession(&session);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = pcvGetClockRate(Clocks::GetPcvModule(module), &hz);
|
||||
ASSERT_RESULT_OK(rc, "pcvGetClockRate");
|
||||
}
|
||||
|
||||
return hz;
|
||||
}
|
||||
|
||||
std::uint32_t Clocks::GetNearestHz(SysClkModule module, SysClkProfile profile, std::uint32_t inHz)
|
||||
{
|
||||
std::uint32_t hz = GetNearestHz(module, inHz);
|
||||
std::uint32_t maxHz = GetMaxAllowedHz(module, profile);
|
||||
|
||||
if(maxHz != 0)
|
||||
{
|
||||
hz = std::min(hz, maxHz);
|
||||
}
|
||||
|
||||
return hz;
|
||||
}
|
||||
|
||||
std::uint32_t Clocks::GetMaxAllowedHz(SysClkModule module, SysClkProfile profile)
|
||||
{
|
||||
if(module == SysClkModule_GPU)
|
||||
{
|
||||
if(profile < SysClkProfile_HandheldCharging)
|
||||
{
|
||||
return isMariko ? 1536000000 : SYSCLK_GPU_HANDHELD_MAX_HZ;
|
||||
}
|
||||
else if(profile <= SysClkProfile_HandheldChargingUSB)
|
||||
{
|
||||
return isMariko ? 1536000000 : SYSCLK_GPU_UNOFFICIAL_CHARGER_MAX_HZ;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::uint32_t Clocks::GetNearestHz(SysClkModule module, std::uint32_t inHz)
|
||||
{
|
||||
// Hardcoded values to return, I don't know why it will bump to max when excessive OC
|
||||
if(module == SysClkModule_MEM)
|
||||
{
|
||||
switch(inHz)
|
||||
{
|
||||
// From Hekate Minerva module
|
||||
case 1331000000:
|
||||
return 1331200000;
|
||||
case 1795000000:
|
||||
return 1795200000;
|
||||
case 1862000000:
|
||||
return 1862400000;
|
||||
case 1894000000:
|
||||
return 1894400000;
|
||||
case 1932000000:
|
||||
return 1932800000;
|
||||
case 1996000000:
|
||||
return 1996800000;
|
||||
case 2099000000:
|
||||
return 2099200000;
|
||||
case 2131000000:
|
||||
return 2131200000;
|
||||
default:
|
||||
return inHz;
|
||||
}
|
||||
}
|
||||
|
||||
if(module == SysClkModule_CPU)
|
||||
{
|
||||
switch(inHz)
|
||||
{
|
||||
case 1963000000:
|
||||
return 1963500000;
|
||||
default:
|
||||
return inHz;
|
||||
}
|
||||
}
|
||||
|
||||
if(module == SysClkModule_GPU)
|
||||
{
|
||||
switch(inHz)
|
||||
{
|
||||
case 76000000:
|
||||
return 76800000;
|
||||
case 153000000:
|
||||
return 153600000;
|
||||
case 230000000:
|
||||
return 230400000;
|
||||
case 307000000:
|
||||
return 307200000;
|
||||
case 460000000:
|
||||
return 460800000;
|
||||
case 537000000:
|
||||
return 537600000;
|
||||
case 614000000:
|
||||
return 614400000;
|
||||
case 691000000:
|
||||
return 691200000;
|
||||
case 844000000:
|
||||
return 844800000;
|
||||
case 921000000:
|
||||
return 921600000;
|
||||
case 998000000:
|
||||
return 998400000;
|
||||
case 1075000000:
|
||||
return 1075200000;
|
||||
case 1228000000:
|
||||
return 1228800000;
|
||||
case 1267000000:
|
||||
return 1267200000;
|
||||
case 1305000000:
|
||||
return 1305600000;
|
||||
case 1382000000:
|
||||
return 1382400000;
|
||||
case 1420000000:
|
||||
return 1420800000;
|
||||
default:
|
||||
return inHz;
|
||||
}
|
||||
}
|
||||
|
||||
return inHz;
|
||||
std::uint32_t *clockTable = NULL;
|
||||
GetList(module, &clockTable);
|
||||
|
||||
if (!clockTable || !clockTable[0])
|
||||
{
|
||||
ERROR_THROW("table lookup failed for SysClkModule: %u", module);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
while(clockTable[i + 1])
|
||||
{
|
||||
if (inHz <= (clockTable[i] + clockTable[i + 1]) / 2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return clockTable[i];
|
||||
}
|
||||
|
||||
std::uint32_t Clocks::GetTemperatureMilli(SysClkThermalSensor sensor)
|
||||
{
|
||||
Result rc;
|
||||
std::int32_t millis = 0;
|
||||
|
||||
if(sensor == SysClkThermalSensor_SOC)
|
||||
{
|
||||
rc = tsGetTemperatureMilliC(TsLocation_External, &millis);
|
||||
ASSERT_RESULT_OK(rc, "tsGetTemperatureMilliC");
|
||||
}
|
||||
else if(sensor == SysClkThermalSensor_PCB)
|
||||
{
|
||||
rc = tsGetTemperatureMilliC(TsLocation_Internal, &millis);
|
||||
ASSERT_RESULT_OK(rc, "tsGetTemperatureMilliC");
|
||||
}
|
||||
else if(sensor == SysClkThermalSensor_Skin)
|
||||
{
|
||||
if(hosversionAtLeast(5,0,0))
|
||||
{
|
||||
rc = tcGetSkinTemperatureMilliC(&millis);
|
||||
ASSERT_RESULT_OK(rc, "tcGetSkinTemperatureMilliC");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_THROW("No such SysClkThermalSensor: %u", sensor);
|
||||
}
|
||||
|
||||
return std::max(0, millis);
|
||||
}
|
||||
38
Source/sys-clk-OC/sysmodule/src/clocks.h
Normal file
38
Source/sys-clk-OC/sysmodule/src/clocks.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <switch.h>
|
||||
#include <sysclk.h>
|
||||
|
||||
class Clocks
|
||||
{
|
||||
public:
|
||||
static bool isMariko;
|
||||
static void Exit();
|
||||
static void Initialize();
|
||||
static void ResetToStock();
|
||||
static SysClkProfile GetCurrentProfile();
|
||||
static std::uint32_t GetCurrentHz(SysClkModule module);
|
||||
static void SetHz(SysClkModule module, std::uint32_t hz);
|
||||
static const char* GetProfileName(SysClkProfile profile, bool pretty);
|
||||
static const char* GetModuleName(SysClkModule module, bool pretty);
|
||||
static const char* GetThermalSensorName(SysClkThermalSensor sensor, bool pretty);
|
||||
static std::uint32_t GetNearestHz(SysClkModule module, SysClkProfile profile, std::uint32_t inHz);
|
||||
static std::uint32_t GetTemperatureMilli(SysClkThermalSensor sensor);
|
||||
|
||||
protected:
|
||||
static PcvModule GetPcvModule(SysClkModule sysclkModule);
|
||||
static PcvModuleId GetPcvModuleId(SysClkModule sysclkModule);
|
||||
static std::uint32_t GetNearestHz(SysClkModule module, std::uint32_t inHz);
|
||||
static void GetList(SysClkModule module, std::uint32_t **outClocks);
|
||||
static std::uint32_t GetMaxAllowedHz(SysClkModule module, SysClkProfile profile);
|
||||
};
|
||||
481
Source/sys-clk-OC/sysmodule/src/config.cpp
Normal file
481
Source/sys-clk-OC/sysmodule/src/config.cpp
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "errors.h"
|
||||
#include "file_utils.h"
|
||||
|
||||
Config::Config(std::string path)
|
||||
{
|
||||
this->path = path;
|
||||
this->loaded = false;
|
||||
this->profileMhzMap = std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t>();
|
||||
this->profileCountMap = std::map<std::uint64_t, std::uint8_t>();
|
||||
this->mtime = 0;
|
||||
this->enabled = false;
|
||||
for(unsigned int i = 0; i < SysClkModule_EnumMax; i++)
|
||||
{
|
||||
this->overrideFreqs[i] = 0;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < SysClkConfigValue_EnumMax; i++)
|
||||
{
|
||||
this->configValues[i] = sysclkDefaultConfigValue((SysClkConfigValue)i);
|
||||
}
|
||||
}
|
||||
|
||||
Config::~Config()
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
this->Close();
|
||||
}
|
||||
|
||||
Config *Config::CreateDefault()
|
||||
{
|
||||
return new Config(FILE_CONFIG_DIR "/config.ini");
|
||||
}
|
||||
|
||||
void Config::Load()
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Reading %s", this->path.c_str());
|
||||
|
||||
this->Close();
|
||||
this->mtime = this->CheckModificationTime();
|
||||
if(!this->mtime)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Error finding file");
|
||||
}
|
||||
else if (!ini_browse(&BrowseIniFunc, this, this->path.c_str()))
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Error loading file");
|
||||
}
|
||||
|
||||
this->loaded = true;
|
||||
}
|
||||
|
||||
void Config::Close()
|
||||
{
|
||||
this->loaded = false;
|
||||
this->profileMhzMap.clear();
|
||||
this->profileCountMap.clear();
|
||||
|
||||
for(unsigned int i = 0; i < SysClkConfigValue_EnumMax; i++)
|
||||
{
|
||||
this->configValues[i] = sysclkDefaultConfigValue((SysClkConfigValue)i);
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::Refresh()
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
if (!this->loaded || this->mtime != this->CheckModificationTime())
|
||||
{
|
||||
this->Load();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Config::HasProfilesLoaded()
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
return this->loaded;
|
||||
}
|
||||
|
||||
time_t Config::CheckModificationTime()
|
||||
{
|
||||
time_t mtime = 0;
|
||||
struct stat st;
|
||||
if (stat(this->path.c_str(), &st) == 0)
|
||||
{
|
||||
mtime = st.st_mtime;
|
||||
}
|
||||
|
||||
return mtime;
|
||||
}
|
||||
|
||||
std::uint32_t Config::FindClockMhz(std::uint64_t tid, SysClkModule module, SysClkProfile profile)
|
||||
{
|
||||
if (this->loaded)
|
||||
{
|
||||
std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t>::const_iterator it = this->profileMhzMap.find(std::make_tuple(tid, profile, module));
|
||||
if (it != this->profileMhzMap.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::uint32_t Config::FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list<SysClkProfile> profiles)
|
||||
{
|
||||
std::uint32_t mhz = 0;
|
||||
|
||||
if (this->loaded)
|
||||
{
|
||||
for(auto profile: profiles)
|
||||
{
|
||||
mhz = FindClockMhz(tid, module, profile);
|
||||
|
||||
if(mhz)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::max((std::uint32_t)0, mhz * 1000000);
|
||||
}
|
||||
|
||||
std::uint32_t Config::GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
switch(profile)
|
||||
{
|
||||
case SysClkProfile_Handheld:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_Handheld});
|
||||
case SysClkProfile_HandheldCharging:
|
||||
case SysClkProfile_HandheldChargingUSB:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingUSB, SysClkProfile_HandheldCharging, SysClkProfile_Handheld});
|
||||
case SysClkProfile_HandheldChargingOfficial:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingOfficial, SysClkProfile_HandheldCharging, SysClkProfile_Handheld});
|
||||
case SysClkProfile_Docked:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_Docked});
|
||||
default:
|
||||
ERROR_THROW("Unhandled SysClkProfile: %u", profile);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Config::GetProfiles(std::uint64_t tid, SysClkTitleProfileList* out_profiles)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
out_profiles->mhzMap[profile][module] = FindClockMhz(tid, (SysClkModule)module, (SysClkProfile)profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::SetProfiles(std::uint64_t tid, SysClkTitleProfileList* profiles, bool immediate)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
uint8_t numProfiles = 0;
|
||||
|
||||
// String pointer array passed to ini
|
||||
char* iniKeys[SysClkProfile_EnumMax * SysClkModule_EnumMax + 1];
|
||||
char* iniValues[SysClkProfile_EnumMax * SysClkModule_EnumMax + 1];
|
||||
|
||||
// Char arrays to build strings
|
||||
char keysStr[SysClkProfile_EnumMax * SysClkModule_EnumMax * 0x40];
|
||||
char valuesStr[SysClkProfile_EnumMax * SysClkModule_EnumMax * 0x10];
|
||||
char section[17] = {0};
|
||||
|
||||
// Iteration pointers
|
||||
char** ik = &iniKeys[0];
|
||||
char** iv = &iniValues[0];
|
||||
char* sk = &keysStr[0];
|
||||
char* sv = &valuesStr[0];
|
||||
std::uint32_t* mhz = &profiles->mhz[0];
|
||||
|
||||
snprintf(section, sizeof(section), "%016lX", tid);
|
||||
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
if(*mhz)
|
||||
{
|
||||
numProfiles++;
|
||||
|
||||
// Put key and value as string
|
||||
snprintf(sk, 0x40, "%s_%s", Clocks::GetProfileName((SysClkProfile)profile, false), Clocks::GetModuleName((SysClkModule)module, false));
|
||||
snprintf(sv, 0x10, "%d", *mhz);
|
||||
|
||||
// Add them to the ini key/value str arrays
|
||||
*ik = sk;
|
||||
*iv = sv;
|
||||
ik++;
|
||||
iv++;
|
||||
|
||||
// We used those chars, get to the next ones
|
||||
sk += 0x40;
|
||||
sv += 0x10;
|
||||
}
|
||||
|
||||
mhz++;
|
||||
}
|
||||
}
|
||||
|
||||
*ik = NULL;
|
||||
*iv = NULL;
|
||||
|
||||
if(!ini_putsection(section, (const char**)iniKeys, (const char**)iniValues, this->path.c_str()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only actually apply changes in memory after a succesful save
|
||||
if(immediate)
|
||||
{
|
||||
mhz = &profiles->mhz[0];
|
||||
this->profileCountMap[tid] = numProfiles;
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
if(*mhz)
|
||||
{
|
||||
this->profileMhzMap[std::make_tuple(tid, (SysClkProfile)profile, (SysClkModule)module)] = *mhz;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->profileMhzMap.erase(std::make_tuple(tid, (SysClkProfile)profile, (SysClkModule)module));
|
||||
}
|
||||
mhz++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::uint8_t Config::GetProfileCount(std::uint64_t tid)
|
||||
{
|
||||
std::map<std::uint64_t, std::uint8_t>::iterator it = this->profileCountMap.find(tid);
|
||||
if (it == this->profileCountMap.end())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
int Config::BrowseIniFunc(const char* section, const char* key, const char* value, void *userdata)
|
||||
{
|
||||
Config* config = (Config*)userdata;
|
||||
std::uint64_t input;
|
||||
if(!strcmp(section, CONFIG_VAL_SECTION))
|
||||
{
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
if(!strcmp(key, sysclkFormatConfigValue((SysClkConfigValue)kval, false)))
|
||||
{
|
||||
input = strtoul(value, NULL, 0);
|
||||
if(!sysclkValidConfigValue((SysClkConfigValue)kval, input))
|
||||
{
|
||||
input = sysclkDefaultConfigValue((SysClkConfigValue)kval);
|
||||
FileUtils::LogLine("[cfg] Invalid value for key '%s' in section '%s': using default %d", key, section, input);
|
||||
}
|
||||
config->configValues[kval] = input;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized config value", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::uint64_t tid = strtoul(section, NULL, 16);
|
||||
|
||||
if(!tid || strlen(section) != 16)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid TitleID", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SysClkProfile parsedProfile = SysClkProfile_EnumMax;
|
||||
SysClkModule parsedModule = SysClkModule_EnumMax;
|
||||
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
const char* profileCode = Clocks::GetProfileName((SysClkProfile)profile, false);
|
||||
size_t profileCodeLen = strlen(profileCode);
|
||||
|
||||
if(!strncmp(key, profileCode, profileCodeLen) && key[profileCodeLen] == '_')
|
||||
{
|
||||
const char* subkey = key + profileCodeLen + 1;
|
||||
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
const char* moduleCode = Clocks::GetModuleName((SysClkModule)module, false);
|
||||
size_t moduleCodeLen = strlen(moduleCode);
|
||||
if(!strncmp(subkey, moduleCode, moduleCodeLen) && subkey[moduleCodeLen] == '\0')
|
||||
{
|
||||
parsedProfile = (SysClkProfile)profile;
|
||||
parsedModule = (SysClkModule)module;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(parsedModule == SysClkModule_EnumMax || parsedProfile == SysClkProfile_EnumMax)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized key", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::uint32_t mhz = strtoul(value, NULL, 10);
|
||||
if(!mhz)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid value", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
config->profileMhzMap[std::make_tuple(tid, parsedProfile, parsedModule)] = mhz;
|
||||
std::map<std::uint64_t, std::uint8_t>::iterator it = config->profileCountMap.find(tid);
|
||||
if (it == config->profileCountMap.end())
|
||||
{
|
||||
config->profileCountMap[tid] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
it->second++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Config::SetEnabled(bool enabled)
|
||||
{
|
||||
this->enabled = enabled;
|
||||
}
|
||||
|
||||
bool Config::Enabled()
|
||||
{
|
||||
return this->enabled;
|
||||
}
|
||||
|
||||
void Config::SetOverrideHz(SysClkModule module, std::uint32_t hz)
|
||||
{
|
||||
std::scoped_lock lock{this->overrideMutex};
|
||||
if(!SYSCLK_ENUM_VALID(SysClkModule, module))
|
||||
{
|
||||
ERROR_THROW("Unhandled SysClkModule: %u", module);
|
||||
}
|
||||
this->overrideFreqs[module] = hz;
|
||||
}
|
||||
|
||||
std::uint32_t Config::GetOverrideHz(SysClkModule module)
|
||||
{
|
||||
std::scoped_lock lock{this->overrideMutex};
|
||||
if(!SYSCLK_ENUM_VALID(SysClkModule, module))
|
||||
{
|
||||
ERROR_THROW("Unhandled SysClkModule: %u", module);
|
||||
}
|
||||
|
||||
return this->overrideFreqs[module];
|
||||
}
|
||||
|
||||
std::uint64_t Config::GetConfigValue(SysClkConfigValue kval)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
if(!SYSCLK_ENUM_VALID(SysClkConfigValue, kval))
|
||||
{
|
||||
ERROR_THROW("Unhandled SysClkConfigValue: %u", kval);
|
||||
}
|
||||
|
||||
return this->configValues[kval];
|
||||
}
|
||||
|
||||
const char* Config::GetConfigValueName(SysClkConfigValue kval, bool pretty)
|
||||
{
|
||||
const char* result = sysclkFormatConfigValue(kval, pretty);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
ERROR_THROW("No such SysClkConfigValue: %u", kval);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Config::GetConfigValues(SysClkConfigValueList* out_configValues)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
out_configValues->values[kval] = this->configValues[kval];
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::SetConfigValues(SysClkConfigValueList* configValues, bool immediate)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
|
||||
// String pointer array passed to ini
|
||||
const char* iniKeys[SysClkConfigValue_EnumMax + 1];
|
||||
char* iniValues[SysClkConfigValue_EnumMax + 1];
|
||||
|
||||
// char arrays to build strings
|
||||
char valuesStr[SysClkConfigValue_EnumMax * 0x20];
|
||||
|
||||
// Iteration pointers
|
||||
char* sv = &valuesStr[0];
|
||||
const char** ik = &iniKeys[0];
|
||||
char** iv = &iniValues[0];
|
||||
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
if(!sysclkValidConfigValue((SysClkConfigValue)kval, configValues->values[kval]) || configValues->values[kval] == sysclkDefaultConfigValue((SysClkConfigValue)kval))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Put key and value as string
|
||||
// And add them to the ini key/value str arrays
|
||||
snprintf(sv, 0x20, "%ld", configValues->values[kval]);
|
||||
*ik = sysclkFormatConfigValue((SysClkConfigValue)kval, false);
|
||||
*iv = sv;
|
||||
|
||||
// We used those chars, get to the next ones
|
||||
sv += 0x20;
|
||||
ik++;
|
||||
iv++;
|
||||
}
|
||||
|
||||
*ik = NULL;
|
||||
*iv = NULL;
|
||||
|
||||
if(!ini_putsection(CONFIG_VAL_SECTION, (const char**)iniKeys, (const char**)iniValues, this->path.c_str()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only actually apply changes in memory after a succesful save
|
||||
if(immediate)
|
||||
{
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
if(sysclkValidConfigValue((SysClkConfigValue)kval, configValues->values[kval]))
|
||||
{
|
||||
this->configValues[kval] = configValues->values[kval];
|
||||
}
|
||||
else
|
||||
{
|
||||
this->configValues[kval] = sysclkDefaultConfigValue((SysClkConfigValue)kval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
69
Source/sys-clk-OC/sysmodule/src/config.h
Normal file
69
Source/sys-clk-OC/sysmodule/src/config.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <initializer_list>
|
||||
#include <switch.h>
|
||||
#include <minIni.h>
|
||||
#include <nxExt.h>
|
||||
#include "clocks.h"
|
||||
|
||||
#define CONFIG_VAL_SECTION "values"
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
Config(std::string path);
|
||||
virtual ~Config();
|
||||
|
||||
static Config *CreateDefault();
|
||||
|
||||
bool Refresh();
|
||||
|
||||
bool HasProfilesLoaded();
|
||||
|
||||
std::uint8_t GetProfileCount(std::uint64_t tid);
|
||||
void GetProfiles(std::uint64_t tid, SysClkTitleProfileList* out_profiles);
|
||||
bool SetProfiles(std::uint64_t tid, SysClkTitleProfileList* profiles, bool immediate);
|
||||
std::uint32_t GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile);
|
||||
|
||||
void SetEnabled(bool enabled);
|
||||
bool Enabled();
|
||||
void SetOverrideHz(SysClkModule module, std::uint32_t hz);
|
||||
std::uint32_t GetOverrideHz(SysClkModule module);
|
||||
|
||||
std::uint64_t GetConfigValue(SysClkConfigValue val);
|
||||
const char* GetConfigValueName(SysClkConfigValue val, bool pretty);
|
||||
void GetConfigValues(SysClkConfigValueList* out_configValues);
|
||||
bool SetConfigValues(SysClkConfigValueList* configValues, bool immediate);
|
||||
protected:
|
||||
void Load();
|
||||
void Close();
|
||||
|
||||
time_t CheckModificationTime();
|
||||
std::uint32_t FindClockMhz(std::uint64_t tid, SysClkModule module, SysClkProfile profile);
|
||||
std::uint32_t FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list<SysClkProfile> profiles);
|
||||
static int BrowseIniFunc(const char* section, const char* key, const char* value, void *userdata);
|
||||
|
||||
std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t> profileMhzMap;
|
||||
std::map<std::uint64_t, std::uint8_t> profileCountMap;
|
||||
bool loaded;
|
||||
std::string path;
|
||||
time_t mtime;
|
||||
LockableMutex configMutex;
|
||||
LockableMutex overrideMutex;
|
||||
std::atomic_bool enabled;
|
||||
std::uint32_t overrideFreqs[SysClkModule_EnumMax];
|
||||
std::uint64_t configValues[SysClkConfigValue_EnumMax];
|
||||
};
|
||||
37
Source/sys-clk-OC/sysmodule/src/errors.cpp
Normal file
37
Source/sys-clk-OC/sysmodule/src/errors.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "errors.h"
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
|
||||
void Errors::ThrowException(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
const char *msg = Errors::FormatMessage(format, args);
|
||||
va_end(args);
|
||||
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
const char *Errors::FormatMessage(const char *format, va_list args)
|
||||
{
|
||||
size_t len = vsnprintf(NULL, 0, format, args) * sizeof(char);
|
||||
char *buf = (char *)malloc(len + 1);
|
||||
if (buf == NULL)
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
vsnprintf(buf, len + 1, format, args);
|
||||
|
||||
return buf;
|
||||
}
|
||||
31
Source/sys-clk-OC/sysmodule/src/errors.h
Normal file
31
Source/sys-clk-OC/sysmodule/src/errors.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <switch.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#define ERROR_THROW(format, ...) Errors::ThrowException(format "\n in %s:%u", ##__VA_ARGS__, __FILE__, __LINE__)
|
||||
#define ERROR_RESULT_THROW(rc, format, ...) ERROR_THROW(format "\n RC: [0x%x] %04d-%04d", ##__VA_ARGS__, rc, R_MODULE(rc), R_DESCRIPTION(rc))
|
||||
#define ASSERT_RESULT_OK(rc, format, ...) \
|
||||
if (R_FAILED(rc)) \
|
||||
{ \
|
||||
ERROR_RESULT_THROW(rc, "ASSERT_RESULT_OK: " format, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
class Errors
|
||||
{
|
||||
public:
|
||||
static void ThrowException(const char *format, ...);
|
||||
|
||||
protected:
|
||||
static const char *FormatMessage(const char *format, va_list args);
|
||||
};
|
||||
248
Source/sys-clk-OC/sysmodule/src/file_utils.cpp
Normal file
248
Source/sys-clk-OC/sysmodule/src/file_utils.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "file_utils.h"
|
||||
#include "clocks.h"
|
||||
#include <dirent.h>
|
||||
#include <filesystem>
|
||||
#include <nxExt.h>
|
||||
|
||||
static LockableMutex g_log_mutex;
|
||||
static LockableMutex g_csv_mutex;
|
||||
static std::atomic_bool g_has_initialized = false;
|
||||
static bool g_log_enabled = false;
|
||||
static bool g_boost_enabled = false;
|
||||
static bool g_boost_start_enabled = false;
|
||||
static bool g_downclock_dock_enabled = false;
|
||||
static bool g_reversenx_enabled = false;
|
||||
static std::uint64_t g_last_flag_check = 0;
|
||||
|
||||
extern "C" void __libnx_init_time(void);
|
||||
|
||||
static void _FileUtils_InitializeThreadFunc(void *args)
|
||||
{
|
||||
FileUtils::Initialize();
|
||||
}
|
||||
|
||||
bool FileUtils::IsInitialized()
|
||||
{
|
||||
return g_has_initialized;
|
||||
}
|
||||
|
||||
void FileUtils::LogLine(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (g_has_initialized)
|
||||
{
|
||||
g_log_mutex.Lock();
|
||||
|
||||
FileUtils::RefreshFlags(false);
|
||||
|
||||
if(g_log_enabled)
|
||||
{
|
||||
FILE *file = fopen(FILE_LOG_FILE_PATH, "a");
|
||||
|
||||
if (file)
|
||||
{
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
struct tm* nowTm = localtime(&now.tv_sec);
|
||||
|
||||
fprintf(file, "[%04d-%02d-%02d %02d:%02d:%02d.%03ld] ", nowTm->tm_year+1900, nowTm->tm_mon+1, nowTm->tm_mday, nowTm->tm_hour, nowTm->tm_min, nowTm->tm_sec, now.tv_nsec / 1000000UL);
|
||||
vfprintf(file, format, args);
|
||||
fprintf(file, "\n");
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
g_log_mutex.Unlock();
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void FileUtils::WriteContextToCsv(const SysClkContext* context)
|
||||
{
|
||||
std::scoped_lock lock{g_csv_mutex};
|
||||
|
||||
FILE *file = fopen(FILE_CONTEXT_CSV_PATH, "a");
|
||||
|
||||
if (file)
|
||||
{
|
||||
// Print header
|
||||
if(!ftell(file))
|
||||
{
|
||||
fprintf(file, "timestamp,profile,app_tid");
|
||||
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
fprintf(file, ",%s_hz", sysclkFormatModule((SysClkModule)module, false));
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++)
|
||||
{
|
||||
fprintf(file, ",%s_milliC", sysclkFormatThermalSensor((SysClkThermalSensor)sensor, false));
|
||||
}
|
||||
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
|
||||
fprintf(file, "%ld%03ld,%s,%016lx", now.tv_sec, now.tv_nsec / 1000000UL, sysclkFormatProfile(context->profile, false), context->applicationId);
|
||||
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
fprintf(file, ",%d", context->freqs[module]);
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++)
|
||||
{
|
||||
fprintf(file, ",%d", context->temps[sensor]);
|
||||
}
|
||||
|
||||
fprintf(file, "\n");
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
void FileUtils::RefreshFlags(bool force)
|
||||
{
|
||||
std::uint64_t now = armTicksToNs(armGetSystemTick());
|
||||
if(!force && (now - g_last_flag_check) < FILE_FLAG_CHECK_INTERVAL_NS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *file = fopen(FILE_LOG_FLAG_PATH, "r");
|
||||
if (file)
|
||||
{
|
||||
g_log_enabled = true;
|
||||
fclose(file);
|
||||
} else {
|
||||
g_log_enabled = false;
|
||||
}
|
||||
|
||||
// Only Enable Boost for Mariko
|
||||
if (Clocks::isMariko)
|
||||
{
|
||||
file = fopen(FILE_BOOST_FLAG_PATH, "r");
|
||||
if (file)
|
||||
{
|
||||
g_boost_enabled = true;
|
||||
fclose(file);
|
||||
} else {
|
||||
g_boost_enabled = false;
|
||||
}
|
||||
|
||||
file = fopen(FILE_BOOST_START_FLAG_PATH, "r");
|
||||
if (file)
|
||||
{
|
||||
g_boost_start_enabled = true;
|
||||
fclose(file);
|
||||
} else {
|
||||
g_boost_start_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
file = fopen(FILE_DOWNCLOCK_DOCK_FLAG_PATH, "r");
|
||||
if (file)
|
||||
{
|
||||
g_downclock_dock_enabled = true;
|
||||
fclose(file);
|
||||
} else {
|
||||
g_downclock_dock_enabled = false;
|
||||
}
|
||||
|
||||
g_last_flag_check = now;
|
||||
}
|
||||
|
||||
bool FileUtils::IsBoostEnabled()
|
||||
{
|
||||
return g_boost_enabled;
|
||||
}
|
||||
|
||||
bool FileUtils::IsBoostStartEnabled()
|
||||
{
|
||||
return g_boost_start_enabled;
|
||||
}
|
||||
|
||||
bool FileUtils::IsDownclockDockEnabled()
|
||||
{
|
||||
return g_downclock_dock_enabled;
|
||||
}
|
||||
|
||||
void FileUtils::InitializeAsync()
|
||||
{
|
||||
Thread initThread = {0};
|
||||
threadCreate(&initThread, _FileUtils_InitializeThreadFunc, NULL, NULL, 0x4000, 0x15, 0);
|
||||
threadStart(&initThread);
|
||||
}
|
||||
|
||||
Result FileUtils::Initialize()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = timeInitialize();
|
||||
}
|
||||
|
||||
__libnx_init_time();
|
||||
timeExit();
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = fsInitialize();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = fsdevMountSdmc();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
FileUtils::RefreshFlags(true);
|
||||
g_has_initialized = true;
|
||||
FileUtils::LogLine("=== " TARGET " " TARGET_VERSION " ===");
|
||||
}
|
||||
|
||||
FILE *file = fopen(FILE_SALTYNX_PATH, "r");
|
||||
if (file)
|
||||
{
|
||||
g_reversenx_enabled = true;
|
||||
fclose(file);
|
||||
} else {
|
||||
g_reversenx_enabled = false;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool FileUtils::IsReverseNXEnabled()
|
||||
{
|
||||
return g_reversenx_enabled;
|
||||
}
|
||||
|
||||
void FileUtils::Exit()
|
||||
{
|
||||
if (!g_has_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_has_initialized = false;
|
||||
g_log_enabled = false;
|
||||
|
||||
fsdevUnmountAll();
|
||||
fsExit();
|
||||
}
|
||||
47
Source/sys-clk-OC/sysmodule/src/file_utils.h
Normal file
47
Source/sys-clk-OC/sysmodule/src/file_utils.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <cstdarg>
|
||||
#include <sysclk.h>
|
||||
|
||||
#define FILE_CONFIG_DIR "/config/" TARGET
|
||||
#define FILE_FLAG_CHECK_INTERVAL_NS 5000000000ULL
|
||||
#define FILE_CONTEXT_CSV_PATH FILE_CONFIG_DIR "/context.csv"
|
||||
#define FILE_LOG_FLAG_PATH FILE_CONFIG_DIR "/log.flag"
|
||||
#define FILE_LOG_FILE_PATH FILE_CONFIG_DIR "/log.txt"
|
||||
#define FILE_BOOST_FLAG_PATH FILE_CONFIG_DIR "/boost.flag"
|
||||
#define FILE_BOOST_START_FLAG_PATH FILE_CONFIG_DIR "/boost_start.flag"
|
||||
#define FILE_DOWNCLOCK_DOCK_FLAG_PATH FILE_CONFIG_DIR "/downclock_dock.flag"
|
||||
#define FILE_SALTYNX_PATH "/atmosphere/contents/0000000000534C56/flags/boot2.flag" // Just check for SaltyNX boot flag
|
||||
#define FILE_REVERSENX_RT_CONF_PATH FILE_CONFIG_DIR "/ReverseNX-RT.conf"
|
||||
|
||||
class FileUtils
|
||||
{
|
||||
public:
|
||||
static void Exit();
|
||||
static Result Initialize();
|
||||
static bool IsInitialized();
|
||||
static bool IsLogEnabled();
|
||||
static bool IsBoostEnabled();
|
||||
static bool IsBoostStartEnabled();
|
||||
static bool IsDownclockDockEnabled();
|
||||
static bool IsReverseNXEnabled();
|
||||
static void InitializeAsync();
|
||||
static void LogLine(const char *format, ...);
|
||||
static void WriteContextToCsv(const SysClkContext* context);
|
||||
protected:
|
||||
static void RefreshFlags(bool force);
|
||||
};
|
||||
291
Source/sys-clk-OC/sysmodule/src/ipc_service.cpp
Normal file
291
Source/sys-clk-OC/sysmodule/src/ipc_service.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "ipc_service.h"
|
||||
#include <cstring>
|
||||
#include <switch.h>
|
||||
#include "file_utils.h"
|
||||
#include "clock_manager.h"
|
||||
#include "errors.h"
|
||||
|
||||
IpcService::IpcService()
|
||||
{
|
||||
std::int32_t priority;
|
||||
Result rc = svcGetThreadPriority(&priority, CUR_THREAD_HANDLE);
|
||||
ASSERT_RESULT_OK(rc, "svcGetThreadPriority");
|
||||
rc = ipcServerInit(&this->server, SYSCLK_IPC_SERVICE_NAME, 42);
|
||||
ASSERT_RESULT_OK(rc, "ipcServerInit");
|
||||
rc = threadCreate(&this->thread, &IpcService::ProcessThreadFunc, this, NULL, 0x2000, priority, -2);
|
||||
ASSERT_RESULT_OK(rc, "threadCreate");
|
||||
this->running = false;
|
||||
}
|
||||
|
||||
void IpcService::SetRunning(bool running)
|
||||
{
|
||||
std::scoped_lock lock{this->threadMutex};
|
||||
if(this->running == running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->running = running;
|
||||
|
||||
if(running)
|
||||
{
|
||||
Result rc = threadStart(&this->thread);
|
||||
ASSERT_RESULT_OK(rc, "threadStart");
|
||||
}
|
||||
else
|
||||
{
|
||||
svcCancelSynchronization(this->thread.handle);
|
||||
threadWaitForExit(&this->thread);
|
||||
}
|
||||
}
|
||||
|
||||
IpcService::~IpcService()
|
||||
{
|
||||
this->SetRunning(false);
|
||||
Result rc = threadClose(&this->thread);
|
||||
ASSERT_RESULT_OK(rc, "threadClose");
|
||||
rc = ipcServerExit(&this->server);
|
||||
ASSERT_RESULT_OK(rc, "ipcServerExit");
|
||||
}
|
||||
|
||||
void IpcService::ProcessThreadFunc(void *arg)
|
||||
{
|
||||
Result rc;
|
||||
IpcService* ipcSrv = (IpcService*)arg;
|
||||
while(true)
|
||||
{
|
||||
rc = ipcServerProcess(&ipcSrv->server, &IpcService::ServiceHandlerFunc, arg);
|
||||
if(R_FAILED(rc))
|
||||
{
|
||||
if(rc == KERNELRESULT(Cancelled))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(rc != KERNELRESULT(ConnectionClosed))
|
||||
{
|
||||
FileUtils::LogLine("[ipc] ipcServerProcess: [0x%x] %04d-%04d", rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result IpcService::ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8* out_data, size_t* out_dataSize)
|
||||
{
|
||||
IpcService* ipcSrv = (IpcService*)arg;
|
||||
|
||||
switch(r->data.cmdId)
|
||||
{
|
||||
case SysClkIpcCmd_GetApiVersion:
|
||||
*out_dataSize = sizeof(u32);
|
||||
return ipcSrv->GetApiVersion((u32*)out_data);
|
||||
|
||||
case SysClkIpcCmd_GetVersionString:
|
||||
if(r->hipc.meta.num_recv_buffers >= 1)
|
||||
{
|
||||
return ipcSrv->GetVersionString(
|
||||
(char*)hipcGetBufferAddress(r->hipc.data.recv_buffers),
|
||||
hipcGetBufferSize(r->hipc.data.recv_buffers)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetCurrentContext:
|
||||
*out_dataSize = sizeof(SysClkContext);
|
||||
return ipcSrv->GetCurrentContext((SysClkContext*)out_data);
|
||||
|
||||
case SysClkIpcCmd_Exit:
|
||||
return ipcSrv->Exit();
|
||||
|
||||
case SysClkIpcCmd_GetProfileCount:
|
||||
if(r->data.size >= sizeof(std::uint64_t))
|
||||
{
|
||||
*out_dataSize = sizeof(std::uint8_t);
|
||||
return ipcSrv->GetProfileCount((std::uint64_t*)r->data.ptr, (std::uint8_t*)out_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetProfiles:
|
||||
if(r->data.size >= sizeof(std::uint64_t))
|
||||
{
|
||||
*out_dataSize = sizeof(SysClkTitleProfileList);
|
||||
return ipcSrv->GetProfiles((std::uint64_t*)r->data.ptr, (SysClkTitleProfileList*)out_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetProfiles:
|
||||
if(r->data.size >= sizeof(SysClkIpc_SetProfiles_Args))
|
||||
{
|
||||
return ipcSrv->SetProfiles((SysClkIpc_SetProfiles_Args*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetEnabled:
|
||||
if(r->data.size >= sizeof(std::uint8_t))
|
||||
{
|
||||
return ipcSrv->SetEnabled((std::uint8_t*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetOverride:
|
||||
if(r->data.size >= sizeof(SysClkIpc_SetOverride_Args))
|
||||
{
|
||||
return ipcSrv->SetOverride((SysClkIpc_SetOverride_Args*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetConfigValues:
|
||||
*out_dataSize = sizeof(SysClkTitleProfileList);
|
||||
return ipcSrv->GetConfigValues((SysClkConfigValueList*)out_data);
|
||||
|
||||
case SysClkIpcCmd_SetConfigValues:
|
||||
if(r->data.size >= sizeof(SysClkConfigValueList))
|
||||
{
|
||||
return ipcSrv->SetConfigValues((SysClkConfigValueList*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
|
||||
Result IpcService::GetApiVersion(u32* out_version)
|
||||
{
|
||||
*out_version = SYSCLK_IPC_API_VERSION;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetVersionString(char* out_buf, size_t bufSize)
|
||||
{
|
||||
if(bufSize)
|
||||
{
|
||||
strncpy(out_buf, TARGET_VERSION, bufSize-1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetCurrentContext(SysClkContext* out_ctx)
|
||||
{
|
||||
ClockManager* clockMgr = ClockManager::GetInstance();
|
||||
*out_ctx = clockMgr->GetCurrentContext();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::Exit()
|
||||
{
|
||||
ClockManager* clockMgr = ClockManager::GetInstance();
|
||||
clockMgr->SetRunning(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
*out_count = config->GetProfileCount(*tid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetProfiles(std::uint64_t* tid, SysClkTitleProfileList* out_profiles)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
config->GetProfiles(*tid, out_profiles);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetProfiles(SysClkIpc_SetProfiles_Args* args)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
SysClkTitleProfileList profiles = args->profiles;
|
||||
|
||||
if(!config->SetProfiles(args->tid, &profiles, true))
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigSaveFailed);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetEnabled(std::uint8_t* enabled)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
config->SetEnabled(*enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetOverride(SysClkIpc_SetOverride_Args* args)
|
||||
{
|
||||
SysClkModule module = args->module;
|
||||
std::uint32_t hz = args->hz;
|
||||
|
||||
if(!SYSCLK_ENUM_VALID(SysClkModule, args->module))
|
||||
{
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
config->SetOverrideHz(module, hz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetConfigValues(SysClkConfigValueList* out_configValues)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
config->GetConfigValues(out_configValues);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetConfigValues(SysClkConfigValueList* configValues)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
SysClkConfigValueList configValuesCopy = *configValues;
|
||||
|
||||
if(!config->SetConfigValues(&configValuesCopy, true))
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigSaveFailed);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
44
Source/sys-clk-OC/sysmodule/src/ipc_service.h
Normal file
44
Source/sys-clk-OC/sysmodule/src/ipc_service.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <nxExt.h>
|
||||
#include <sysclk.h>
|
||||
|
||||
class IpcService
|
||||
{
|
||||
public:
|
||||
IpcService();
|
||||
virtual ~IpcService();
|
||||
void SetRunning(bool running);
|
||||
|
||||
protected:
|
||||
static void ProcessThreadFunc(void *arg);
|
||||
static Result ServiceHandlerFunc(void* arg, const IpcServerRequest* r, std::uint8_t* out_data, size_t* out_dataSize);
|
||||
|
||||
Result GetApiVersion(u32* out_version);
|
||||
Result GetVersionString(char* out_buf, size_t bufSize);
|
||||
Result GetCurrentContext(SysClkContext* out_ctx);
|
||||
Result Exit();
|
||||
Result GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count);
|
||||
Result GetProfiles(std::uint64_t* tid, SysClkTitleProfileList* out_profiles);
|
||||
Result SetProfiles(SysClkIpc_SetProfiles_Args* args);
|
||||
Result SetEnabled(std::uint8_t* enabled);
|
||||
Result SetOverride(SysClkIpc_SetOverride_Args* args);
|
||||
Result GetConfigValues(SysClkConfigValueList* out_configValues);
|
||||
Result SetConfigValues(SysClkConfigValueList* configValues);
|
||||
|
||||
bool running;
|
||||
Thread thread;
|
||||
LockableMutex threadMutex;
|
||||
IpcServer server;
|
||||
};
|
||||
124
Source/sys-clk-OC/sysmodule/src/main.cpp
Normal file
124
Source/sys-clk-OC/sysmodule/src/main.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#include "errors.h"
|
||||
#include "file_utils.h"
|
||||
#include "clocks.h"
|
||||
#include "process_management.h"
|
||||
#include "clock_manager.h"
|
||||
#include "ipc_service.h"
|
||||
|
||||
#define INNER_HEAP_SIZE 0x30000
|
||||
|
||||
extern "C"
|
||||
{
|
||||
extern std::uint32_t __start__;
|
||||
|
||||
std::uint32_t __nx_applet_type = AppletType_None;
|
||||
TimeServiceType __nx_time_service_type = TimeServiceType_System;
|
||||
std::uint32_t __nx_fs_num_sessions = 1;
|
||||
|
||||
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
||||
char nx_inner_heap[INNER_HEAP_SIZE];
|
||||
|
||||
void __libnx_initheap(void)
|
||||
{
|
||||
void *addr = nx_inner_heap;
|
||||
size_t size = nx_inner_heap_size;
|
||||
|
||||
/* Newlib Heap Management */
|
||||
extern char *fake_heap_start;
|
||||
extern char *fake_heap_end;
|
||||
|
||||
fake_heap_start = (char *)addr;
|
||||
fake_heap_end = (char *)addr + size;
|
||||
}
|
||||
|
||||
void __appInit(void)
|
||||
{
|
||||
if (R_FAILED(smInitialize()))
|
||||
{
|
||||
fatalThrow(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM));
|
||||
}
|
||||
|
||||
Result rc = setsysInitialize();
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
SetSysFirmwareVersion fw;
|
||||
rc = setsysGetFirmwareVersion(&fw);
|
||||
if (R_SUCCEEDED(rc))
|
||||
hosversionSet(MAKEHOSVERSION(fw.major, fw.minor, fw.micro));
|
||||
setsysExit();
|
||||
}
|
||||
}
|
||||
|
||||
void __appExit(void)
|
||||
{
|
||||
smExit();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Result rc = FileUtils::Initialize();
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
fatalThrow(rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Clocks::Initialize();
|
||||
ProcessManagement::Initialize();
|
||||
|
||||
ProcessManagement::WaitForQLaunch();
|
||||
ClockManager::Initialize();
|
||||
FileUtils::LogLine("Ready");
|
||||
|
||||
ClockManager *clockMgr = ClockManager::GetInstance();
|
||||
IpcService *ipcSrv = new IpcService();
|
||||
clockMgr->SetRunning(true);
|
||||
clockMgr->GetConfig()->SetEnabled(true);
|
||||
ipcSrv->SetRunning(true);
|
||||
|
||||
while (clockMgr->Running())
|
||||
{
|
||||
clockMgr->Tick();
|
||||
clockMgr->WaitForNextTick();
|
||||
}
|
||||
|
||||
ipcSrv->SetRunning(false);
|
||||
delete ipcSrv;
|
||||
ClockManager::Exit();
|
||||
ProcessManagement::Exit();
|
||||
Clocks::Exit();
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
FileUtils::LogLine("[!] %s", ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::exception_ptr p = std::current_exception();
|
||||
FileUtils::LogLine("[!?] %s", p ? p.__cxa_exception_type()->name() : "...");
|
||||
}
|
||||
|
||||
FileUtils::LogLine("Exit");
|
||||
svcSleepThread(1000000ULL);
|
||||
FileUtils::Exit();
|
||||
return 0;
|
||||
}
|
||||
67
Source/sys-clk-OC/sysmodule/src/process_management.cpp
Normal file
67
Source/sys-clk-OC/sysmodule/src/process_management.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "process_management.h"
|
||||
#include "file_utils.h"
|
||||
#include "errors.h"
|
||||
|
||||
void ProcessManagement::Initialize()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
rc = pmdmntInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pmdmntInitialize");
|
||||
|
||||
rc = pminfoInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pminfoInitialize");
|
||||
}
|
||||
|
||||
void ProcessManagement::WaitForQLaunch()
|
||||
{
|
||||
Result rc = 0;
|
||||
std::uint64_t pid = 0;
|
||||
do
|
||||
{
|
||||
rc = pmdmntGetProcessId(&pid, PROCESS_MANAGEMENT_QLAUNCH_TID);
|
||||
svcSleepThread(500000000ULL);
|
||||
} while (R_FAILED(rc));
|
||||
}
|
||||
|
||||
std::uint64_t ProcessManagement::GetCurrentApplicationId()
|
||||
{
|
||||
Result rc = 0;
|
||||
std::uint64_t pid = 0;
|
||||
std::uint64_t tid = 0;
|
||||
rc = pmdmntGetApplicationProcessId(&pid);
|
||||
|
||||
if (rc == 0x20f)
|
||||
{
|
||||
return PROCESS_MANAGEMENT_QLAUNCH_TID;
|
||||
}
|
||||
|
||||
ASSERT_RESULT_OK(rc, "pmdmntGetApplicationProcessId");
|
||||
|
||||
rc = pminfoGetProgramId(&tid, pid);
|
||||
|
||||
if (rc == 0x20f)
|
||||
{
|
||||
return PROCESS_MANAGEMENT_QLAUNCH_TID;
|
||||
}
|
||||
|
||||
ASSERT_RESULT_OK(rc, "pminfoGetProgramId");
|
||||
|
||||
return tid;
|
||||
}
|
||||
|
||||
void ProcessManagement::Exit()
|
||||
{
|
||||
pmdmntExit();
|
||||
pminfoExit();
|
||||
}
|
||||
24
Source/sys-clk-OC/sysmodule/src/process_management.h
Normal file
24
Source/sys-clk-OC/sysmodule/src/process_management.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <cstdint>
|
||||
|
||||
#define PROCESS_MANAGEMENT_QLAUNCH_TID 0x0100000000001000ULL
|
||||
|
||||
class ProcessManagement
|
||||
{
|
||||
public:
|
||||
static void Initialize();
|
||||
static void WaitForQLaunch();
|
||||
static std::uint64_t GetCurrentApplicationId();
|
||||
static void Exit();
|
||||
};
|
||||
Reference in New Issue
Block a user