- Revert commit a45b767a80 in favor of tinkering "enough_power_threshold_mw" in system-settings.ini
- Add InfoNX (For battery, charger, clocks, voltages, etc.)
This commit is contained in:
411
Source/InfoNX/source/main.cpp
Normal file
411
Source/InfoNX/source/main.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
#define TESLA_INIT_IMPL // If you have more than one file using the tesla header, only define this in the main one
|
||||
#include <tesla.hpp> // The Tesla Header
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <switch.h>
|
||||
#include "rgltr.h"
|
||||
|
||||
#define IS_BAT_CHARGE_ON ((_batteryChargeInfoFields->unk_x14 >> 8) & 1)
|
||||
#define IS_SLOW_CHARGE_ON (_batteryChargeInfoFields->ChargeCurrentLimit < 1024)
|
||||
|
||||
static Service g_rgltrSrv;
|
||||
|
||||
Result rgltrInitialize(void) {
|
||||
if(hosversionBefore(8,0,0))
|
||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
||||
|
||||
return smGetService(&g_rgltrSrv, "rgltr");
|
||||
}
|
||||
|
||||
void rgltrExit(void) {
|
||||
serviceClose(&g_rgltrSrv);
|
||||
}
|
||||
|
||||
Service* rgltrGetServiceSession(void) {
|
||||
return &g_rgltrSrv;
|
||||
}
|
||||
|
||||
Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id) {
|
||||
const u32 in = module_id;
|
||||
return serviceDispatchIn(&g_rgltrSrv, 0, in,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &session_out->s,
|
||||
);
|
||||
}
|
||||
|
||||
Result rgltrGetPowerModuleNumLimit(u32 *out) {
|
||||
return serviceDispatchOut(&g_rgltrSrv, 3, *out);
|
||||
}
|
||||
|
||||
void rgltrCloseSession(RgltrSession* session) {
|
||||
serviceClose(&session->s);
|
||||
}
|
||||
|
||||
Result rgltrGetVoltageEnabled(RgltrSession* session, u32 *out) {
|
||||
return serviceDispatchOut(&session->s, 2, *out);
|
||||
}
|
||||
|
||||
Result rgltrGetVoltage(RgltrSession* session, u32 *out_volt) {
|
||||
return serviceDispatchOut(&session->s, 4, *out_volt);
|
||||
}
|
||||
|
||||
///* Notes VoltageAvg
|
||||
//
|
||||
// Vavg time = 175.8ms x 2^(6+VOLT), default: VOLT = 2 (Vavg time = 45s)
|
||||
//
|
||||
///End of Notes
|
||||
|
||||
extern "C" bool is_mariko();
|
||||
extern "C" void clk_check();
|
||||
|
||||
typedef struct {
|
||||
u32 cpu_out_hz;
|
||||
u32 gpu_out_hz;
|
||||
u32 emc_out_hz;
|
||||
u32 cpu_out_volt;
|
||||
u32 gpu_out_volt;
|
||||
u32 emc_out_volt;
|
||||
} ClkFields;
|
||||
|
||||
bool is_mariko() {
|
||||
u64 hardware_type = 0;
|
||||
|
||||
splInitialize();
|
||||
splGetConfig(SplConfigItem_HardwareType, &hardware_type);
|
||||
splExit();
|
||||
|
||||
switch(hardware_type) {
|
||||
case 0: //Icosa
|
||||
case 1: //Copper
|
||||
return false;
|
||||
case 2: //Hoag
|
||||
case 3: //Iowa
|
||||
case 4: //Calcio
|
||||
case 5: //Aula
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void clk_check(ClkFields *out, bool mariko) {
|
||||
int res = 0;
|
||||
|
||||
ClkrstSession clkrstSession;
|
||||
RgltrSession rgltrSession;
|
||||
|
||||
res = clkrstInitialize();
|
||||
if(R_FAILED(res)) {
|
||||
fatalThrow(res);
|
||||
}
|
||||
|
||||
res = rgltrInitialize();
|
||||
if(R_FAILED(res)) {
|
||||
fatalThrow(res);
|
||||
}
|
||||
|
||||
rgltrOpenSession(&rgltrSession, mariko ? PcvPowerDomainId_Max77812_Cpu : PcvPowerDomainId_Max77621_Cpu);
|
||||
clkrstOpenSession(&clkrstSession, PcvModuleId_CpuBus, 3);
|
||||
rgltrGetVoltage(&rgltrSession, &out->cpu_out_volt);
|
||||
clkrstGetClockRate(&clkrstSession, &out->cpu_out_hz);
|
||||
clkrstCloseSession(&clkrstSession);
|
||||
rgltrCloseSession(&rgltrSession);
|
||||
|
||||
rgltrOpenSession(&rgltrSession, mariko ? PcvPowerDomainId_Max77812_Gpu : PcvPowerDomainId_Max77621_Gpu);
|
||||
clkrstOpenSession(&clkrstSession, PcvModuleId_GPU, 3);
|
||||
rgltrGetVoltage(&rgltrSession, &out->gpu_out_volt);
|
||||
clkrstGetClockRate(&clkrstSession, &out->gpu_out_hz);
|
||||
clkrstCloseSession(&clkrstSession);
|
||||
rgltrCloseSession(&rgltrSession);
|
||||
|
||||
rgltrOpenSession(&rgltrSession, mariko ? PcvPowerDomainId_Max77812_Dram : PcvPowerDomainId_Max77620_Sd1);
|
||||
clkrstOpenSession(&clkrstSession, PcvModuleId_EMC, 3);
|
||||
rgltrGetVoltage(&rgltrSession, &out->emc_out_volt);
|
||||
clkrstGetClockRate(&clkrstSession, &out->emc_out_hz);
|
||||
clkrstCloseSession(&clkrstSession);
|
||||
rgltrCloseSession(&rgltrSession);
|
||||
|
||||
clkrstExit();
|
||||
rgltrExit();
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
NoHub = BIT(0), //If hub is disconnected
|
||||
Rail = BIT(8), //At least one Joy-con is charging from rail
|
||||
SPDSRC = BIT(12), //OTG
|
||||
ACC = BIT(16) //Accessory
|
||||
} BatteryChargeInfoFieldsFlags;
|
||||
|
||||
typedef enum {
|
||||
NewPDO = 1, //Received new Power Data Object
|
||||
NoPD = 2, //No Power Delivery source is detected
|
||||
AcceptedRDO = 3 //Received and accepted Request Data Object
|
||||
} BatteryChargeInfoFieldsPDControllerState; //BM92T series
|
||||
|
||||
const char* strPDControllerState[] = {
|
||||
"Received new PDO",
|
||||
"No PD Source",
|
||||
"Received/Accpted RDO"
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
None = 0,
|
||||
PD = 1,
|
||||
TypeC_1500mA = 2,
|
||||
TypeC_3000mA = 3,
|
||||
DCP = 4,
|
||||
CDP = 5,
|
||||
SDP = 6,
|
||||
Apple_500mA = 7,
|
||||
Apple_1000mA = 8,
|
||||
Apple_2000mA = 9
|
||||
} BatteryChargeInfoFieldsChargerType;
|
||||
|
||||
const char* strChargerType[] = {
|
||||
"None",
|
||||
"PD",
|
||||
"USB-C@1.5A",
|
||||
"USB-C@3.0A",
|
||||
"USB-DCP",
|
||||
"USB-CDP",
|
||||
"USB-SDP",
|
||||
"Apple@0.5A",
|
||||
"Apple@1.0A",
|
||||
"Apple@2.0A",
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Sink = 1,
|
||||
Source = 2
|
||||
} BatteryChargeInfoFieldsPowerRole;
|
||||
|
||||
const char* strPowerRole[] = {
|
||||
"Sink",
|
||||
"Source",
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int32_t InputCurrentLimit; //Input (Sink) current limit in mA
|
||||
int32_t VBUSCurrentLimit; //Output (Source/VBUS/OTG) current limit in mA
|
||||
int32_t ChargeCurrentLimit; //Battery charging current limit in mA (512mA when Docked, 768mA when BatteryTemperature < 17.0 C)
|
||||
int32_t ChargeVoltageLimit; //Battery charging voltage limit in mV (3952mV when BatteryTemperature >= 51.0 C)
|
||||
int32_t unk_x10; //Possibly an emum, getting the same value as PowerRole in all tested cases
|
||||
int32_t unk_x14; //Possibly flags
|
||||
BatteryChargeInfoFieldsPDControllerState PDControllerState; //Power Delivery Controller State
|
||||
int32_t BatteryTemperature; //Battery temperature in milli C
|
||||
int32_t RawBatteryCharge; //Raw battery charged capacity per cent-mille (i.e. 100% = 100000 pcm)
|
||||
int32_t VoltageAvg; //Voltage avg in mV (more in Notes)
|
||||
int32_t BatteryAge; //Battery age (capacity full / capacity design) per cent-mille (i.e. 100% = 100000 pcm)
|
||||
BatteryChargeInfoFieldsPowerRole PowerRole;
|
||||
BatteryChargeInfoFieldsChargerType ChargerType;
|
||||
int32_t ChargerVoltageLimit; //Charger and external device voltage limit in mV
|
||||
int32_t ChargerCurrentLimit; //Charger and external device current limit in mA
|
||||
BatteryChargeInfoFieldsFlags Flags; //Unknown flags
|
||||
} BatteryChargeInfoFields;
|
||||
|
||||
Result psmGetBatteryChargeInfoFields(Service* psmService, BatteryChargeInfoFields *out) {
|
||||
return serviceDispatchOut(psmService, 17, *out);
|
||||
}
|
||||
|
||||
static Result psmEnableBatteryCharging(Service* psmService) {
|
||||
return serviceDispatch(psmService, 2);
|
||||
}
|
||||
|
||||
static Result psmDisableBatteryCharging(Service* psmService) {
|
||||
return serviceDispatch(psmService, 3);
|
||||
}
|
||||
|
||||
static Result psmDisableSlowCharging(Service* psmService) {
|
||||
return serviceDispatch(psmService, 10);
|
||||
}
|
||||
|
||||
static Result psmEnableSlowCharging(Service* psmService) {
|
||||
return serviceDispatch(psmService, 11);
|
||||
}
|
||||
|
||||
FanController g_ICon;
|
||||
float rotationSpeedLevel = 0;
|
||||
unsigned int changeSlowChargingState = 0;
|
||||
unsigned int changeDisableChargingState = 0;
|
||||
|
||||
bool threadexit = false;
|
||||
int32_t socTempMili = 0;
|
||||
char Print_x[800];
|
||||
Thread t0;
|
||||
bool IsEnoughPowerSupplied;
|
||||
|
||||
void Loop(void*) {
|
||||
static Service* psmService = psmGetServiceSession();
|
||||
static BatteryChargeInfoFields* _batteryChargeInfoFields = new BatteryChargeInfoFields;
|
||||
static ClkFields* _clkFields = new ClkFields;
|
||||
while (threadexit == false) {
|
||||
if(changeSlowChargingState)
|
||||
{
|
||||
changeSlowChargingState = 0;
|
||||
if(IS_SLOW_CHARGE_ON)
|
||||
psmDisableSlowCharging(psmService);
|
||||
else
|
||||
psmEnableSlowCharging(psmService);
|
||||
}
|
||||
if(changeDisableChargingState)
|
||||
{
|
||||
changeDisableChargingState = 0;
|
||||
if(IS_BAT_CHARGE_ON)
|
||||
psmDisableBatteryCharging(psmService);
|
||||
else
|
||||
psmEnableBatteryCharging(psmService);
|
||||
}
|
||||
|
||||
psmGetBatteryChargeInfoFields(psmService, _batteryChargeInfoFields);
|
||||
clk_check(_clkFields, is_mariko());
|
||||
tsGetTemperatureMilliC(TsLocation_External, &socTempMili);
|
||||
psmIsEnoughPowerSupplied(&IsEnoughPowerSupplied);
|
||||
fanControllerGetRotationSpeedLevel(&g_ICon, &rotationSpeedLevel);
|
||||
|
||||
snprintf(Print_x, sizeof(Print_x),
|
||||
"Current Limit: %u mA IN, %u mA OUT"
|
||||
"\nBattery Charg. Limit: %u mA, %u mV"
|
||||
"\nunk_x10: 0x%08" PRIx32
|
||||
"\nunk_x14: 0x%08" PRIx32
|
||||
"\nPD Contr. State: %s (%u)"
|
||||
"\nBattery Temp.: %.2f\u00B0C"
|
||||
"\nRaw Battery Charge: %.2f%%"
|
||||
"\nVoltage Avg: %u mV"
|
||||
"\nBattery Age: %.2f%%"
|
||||
"\nPower Role: %s (%u)"
|
||||
"\nCharger Type: %s (%u)"
|
||||
"\nCharger Limit: %u mV, %u mA"
|
||||
"\nunk_x3c: 0x%08" PRIx32
|
||||
"\nEnough Power Supplied: %s"
|
||||
"\n"
|
||||
"\nCPU Clock: %6.1f MHz"
|
||||
"\nCPU Volt : %6.1f mV\n"
|
||||
"\nGPU Clock: %6.1f MHz"
|
||||
"\nGPU Volt : %6.1f mV\n"
|
||||
"\nEMC Clock: %6.1f MHz"
|
||||
"\nEMC Volt : %6.1f mV\n"
|
||||
"\nSoC Temp : %2.2f \u00B0C"
|
||||
"\nFan Speed: %2.2f %%\n"
|
||||
"\nL-Stick: Slow Charging(0.5A) (%s)"
|
||||
"\nR-Stick: Disable Charging (%s)"
|
||||
,
|
||||
_batteryChargeInfoFields->InputCurrentLimit,
|
||||
_batteryChargeInfoFields->VBUSCurrentLimit,
|
||||
_batteryChargeInfoFields->ChargeCurrentLimit,
|
||||
_batteryChargeInfoFields->ChargeVoltageLimit,
|
||||
_batteryChargeInfoFields->unk_x10,
|
||||
_batteryChargeInfoFields->unk_x14,
|
||||
strPDControllerState[_batteryChargeInfoFields->PDControllerState], _batteryChargeInfoFields->PDControllerState,
|
||||
(float)_batteryChargeInfoFields->BatteryTemperature / 1000,
|
||||
(float)_batteryChargeInfoFields->RawBatteryCharge / 1000,
|
||||
_batteryChargeInfoFields->VoltageAvg,
|
||||
(float)_batteryChargeInfoFields->BatteryAge / 1000,
|
||||
strPowerRole[_batteryChargeInfoFields->PowerRole], _batteryChargeInfoFields->PowerRole,
|
||||
strChargerType[_batteryChargeInfoFields->ChargerType], _batteryChargeInfoFields->ChargerType,
|
||||
_batteryChargeInfoFields->ChargerVoltageLimit,
|
||||
_batteryChargeInfoFields->ChargerCurrentLimit,
|
||||
(int32_t)_batteryChargeInfoFields->Flags,
|
||||
IsEnoughPowerSupplied ? "Yes" : "No",
|
||||
(double)_clkFields->cpu_out_hz / 1000000,
|
||||
(double)_clkFields->cpu_out_volt / 1000,
|
||||
(double)_clkFields->gpu_out_hz / 1000000,
|
||||
(double)_clkFields->gpu_out_volt / 1000,
|
||||
(double)_clkFields->emc_out_hz / 1000000,
|
||||
(double)_clkFields->emc_out_volt / 1000,
|
||||
(float)socTempMili / 1000,
|
||||
rotationSpeedLevel * 100,
|
||||
IS_SLOW_CHARGE_ON ? "ON" : "OFF",
|
||||
!IS_BAT_CHARGE_ON ? "ON" : "OFF"
|
||||
);
|
||||
svcSleepThread(800'000'000);
|
||||
}
|
||||
delete _batteryChargeInfoFields;
|
||||
delete _clkFields;
|
||||
}
|
||||
|
||||
class GuiTest : public tsl::Gui {
|
||||
public:
|
||||
GuiTest(u8 arg1, u8 arg2, bool arg3) { }
|
||||
|
||||
// Called when this Gui gets loaded to create the UI
|
||||
// Allocate all elements on the heap. libtesla will make sure to clean them up when not needed anymore
|
||||
virtual tsl::elm::Element* createUI() override {
|
||||
// A OverlayFrame is the base element every overlay consists of. This will draw the default Title and Subtitle.
|
||||
// If you need more information in the header or want to change it's look, use a HeaderOverlayFrame.
|
||||
auto frame = new tsl::elm::OverlayFrame("InfoNX", APP_VERSION);
|
||||
|
||||
// A list that can contain sub elements and handles scrolling
|
||||
auto list = new tsl::elm::List();
|
||||
|
||||
list->addItem(new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
|
||||
renderer->drawString(Print_x, false, x, y+20, 15, renderer->a(0xFFFF));
|
||||
}), 500);
|
||||
|
||||
// Add the list to the frame for it to be drawn
|
||||
frame->setContent(list);
|
||||
|
||||
|
||||
// Return the frame to have it become the top level element of this Gui
|
||||
return frame;
|
||||
}
|
||||
|
||||
// Called once every frame to update values
|
||||
virtual void update() override {}
|
||||
|
||||
// Called once every frame to handle inputs not handled by other UI elements
|
||||
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
|
||||
if (keysHeld & HidNpadButton_A) {
|
||||
tsl::hlp::requestForeground(false);
|
||||
return true;
|
||||
}
|
||||
if (keysHeld & HidNpadButton_StickL) {
|
||||
changeSlowChargingState++;
|
||||
return true;
|
||||
}
|
||||
if (keysHeld & HidNpadButton_StickR) {
|
||||
changeDisableChargingState++;
|
||||
return true;
|
||||
}
|
||||
return false; // Return true here to singal the inputs have been consumed
|
||||
}
|
||||
};
|
||||
|
||||
class OverlayTest : public tsl::Overlay {
|
||||
public:
|
||||
// libtesla already initialized fs, hid, pl, pmdmnt, hid:sys and set:sys
|
||||
virtual void initServices() override {
|
||||
smInitialize();
|
||||
psmInitialize();
|
||||
tsInitialize();
|
||||
fanInitialize();
|
||||
fanOpenController(&g_ICon, 0x3D000001);
|
||||
threadCreate(&t0, Loop, NULL, NULL, 0x4000, 0x3F, -2);
|
||||
threadStart(&t0);
|
||||
} // Called at the start to initialize all services necessary for this Overlay
|
||||
|
||||
virtual void exitServices() override {
|
||||
threadexit = true;
|
||||
threadWaitForExit(&t0);
|
||||
threadClose(&t0);
|
||||
fanControllerClose(&g_ICon);
|
||||
fanExit();
|
||||
tsExit();
|
||||
psmExit();
|
||||
smExit();
|
||||
} // Callet at the end to clean up all services previously initialized
|
||||
|
||||
virtual void onShow() override {} // Called before overlay wants to change from invisible to visible state
|
||||
|
||||
virtual void onHide() override {} // Called before overlay wants to change from visible to invisible state
|
||||
|
||||
virtual std::unique_ptr<tsl::Gui> loadInitialGui() override {
|
||||
return initially<GuiTest>(1, 2, true); // Initial Gui to load. It's possible to pass arguments to it's constructor like this
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return tsl::loop<OverlayTest>(argc, argv);
|
||||
}
|
||||
Reference in New Issue
Block a user