250 lines
6.8 KiB
C
250 lines
6.8 KiB
C
#include "fancontrol.h"
|
|
#include "tmp451.h"
|
|
|
|
//Fan curve table
|
|
TemperaturePoint defaultTable[] =
|
|
{
|
|
{ .temperature_c = 25.0, .fanLevel_f = 0.00 },
|
|
{ .temperature_c = 30.0, .fanLevel_f = 0.00 },
|
|
{ .temperature_c = 35.0, .fanLevel_f = 0.00 },
|
|
{ .temperature_c = 40.0, .fanLevel_f = 0.00 },
|
|
{ .temperature_c = 45.0, .fanLevel_f = 0.30 },
|
|
{ .temperature_c = 50.0, .fanLevel_f = 0.50 },
|
|
{ .temperature_c = 55.0, .fanLevel_f = 0.60 },
|
|
{ .temperature_c = 60.0, .fanLevel_f = 0.85 },
|
|
{ .temperature_c = 65.0, .fanLevel_f = 0.95 },
|
|
{ .temperature_c = 70.0, .fanLevel_f = 1.00 }
|
|
};
|
|
|
|
|
|
TemperaturePoint *fanControllerTable;
|
|
|
|
//Fan
|
|
Thread FanControllerThread;
|
|
bool fanControllerThreadExit = false;
|
|
|
|
//Log
|
|
char logPath[PATH_MAX];
|
|
|
|
//Power management
|
|
Event powerStateChangeEvent;
|
|
bool isPowerStateInitialized = false;
|
|
|
|
void CreateDir(char *dir)
|
|
{
|
|
char dirPath[PATH_MAX];
|
|
|
|
for(int i = 0; i < PATH_MAX; i++)
|
|
{
|
|
if(*(dir + i) == '/' && access(dirPath, F_OK) == -1)
|
|
{
|
|
mkdir(dirPath, 0777);
|
|
}
|
|
dirPath[i] = *(dir + i);
|
|
}
|
|
}
|
|
|
|
void InitLog()
|
|
{
|
|
if(access(LOG_DIR, F_OK) == -1)
|
|
CreateDir(LOG_DIR);
|
|
|
|
if(access(LOG_FILE, F_OK) != -1)
|
|
remove(LOG_FILE);
|
|
}
|
|
|
|
void WriteLog(char *buffer)
|
|
{
|
|
FILE *log = fopen(LOG_FILE, "a");
|
|
if(log != NULL)
|
|
{
|
|
fprintf(log, "%s\n", buffer);
|
|
}
|
|
fclose(log);
|
|
}
|
|
|
|
void WriteConfigFile(TemperaturePoint *table)
|
|
{
|
|
if(table == NULL)
|
|
{
|
|
table = malloc(sizeof(defaultTable));
|
|
memcpy(table, defaultTable, sizeof(defaultTable));
|
|
}
|
|
|
|
if(access(CONFIG_DIR, F_OK) == -1)
|
|
CreateDir(CONFIG_DIR);
|
|
|
|
FILE *config = fopen(CONFIG_FILE, "w");
|
|
fwrite(table, TABLE_SIZE, 1, config);
|
|
fclose(config);
|
|
}
|
|
|
|
|
|
|
|
void ReadConfigFile(TemperaturePoint **table_out)
|
|
{
|
|
InitLog();
|
|
|
|
*table_out = malloc(sizeof(defaultTable));
|
|
memcpy(*table_out, defaultTable, sizeof(defaultTable));
|
|
|
|
if(access(CONFIG_DIR, F_OK) == -1)
|
|
{
|
|
CreateDir(CONFIG_DIR);
|
|
WriteConfigFile(NULL);
|
|
}
|
|
else
|
|
{
|
|
if(access(CONFIG_FILE, F_OK) == -1)
|
|
{
|
|
WriteConfigFile(NULL);
|
|
}
|
|
else
|
|
{
|
|
FILE *config = fopen(CONFIG_FILE, "r");
|
|
fread(*table_out, TABLE_SIZE, 1, config);
|
|
fclose(config);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IsSystemAwake()
|
|
{
|
|
// Check if system is in sleep mode by checking applet state
|
|
// appletGetOperationMode returns the current operation mode
|
|
AppletOperationMode opMode = appletGetOperationMode();
|
|
|
|
// If in handheld or console mode, system is awake
|
|
// If in any other mode (like sleep), we consider it asleep
|
|
return (opMode == AppletOperationMode_Handheld || opMode == AppletOperationMode_Console);
|
|
}
|
|
|
|
void InitFanController(TemperaturePoint *table)
|
|
{
|
|
fanControllerTable = table;
|
|
|
|
if(R_FAILED(threadCreate(&FanControllerThread, FanControllerThreadFunction, NULL, NULL, 0x4000, 0x3F, -2)))
|
|
{
|
|
WriteLog("Error creating FanControllerThread");
|
|
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
|
}
|
|
}
|
|
|
|
void FanControllerThreadFunction(void*)
|
|
{
|
|
FanController fc;
|
|
float fanLevelSet_f = 0;
|
|
float temperatureC_f = 0;
|
|
u64 awakeSleepTime = 1000000000ULL; // 1 second when awake
|
|
u64 sleepSleepTime = 10000000000ULL; // 10 seconds when in sleep
|
|
int sleepCheckCounter = 0;
|
|
|
|
Result rs = fanOpenController(&fc, 0x3D000001);
|
|
if(R_FAILED(rs))
|
|
{
|
|
WriteLog("Error opening fanController");
|
|
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
|
}
|
|
|
|
while(!fanControllerThreadExit)
|
|
{
|
|
// Check if system is awake every 40 iterations (~10 seconds) to reduce overhead
|
|
sleepCheckCounter++;
|
|
if(sleepCheckCounter >= 40)
|
|
{
|
|
bool isAwake = IsSystemAwake();
|
|
sleepCheckCounter = 0;
|
|
|
|
// If system is asleep, use longer sleep interval
|
|
if(!isAwake)
|
|
{
|
|
svcSleepThread(sleepSleepTime);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
rs = Tmp451GetSocTemp(&temperatureC_f);
|
|
if(R_FAILED(rs))
|
|
{
|
|
WriteLog("tsSessionGetTemperature error");
|
|
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
|
}
|
|
|
|
if(temperatureC_f >= 0 && temperatureC_f <= fanControllerTable->temperature_c)
|
|
{
|
|
float m = 0;
|
|
float q = 0;
|
|
|
|
m = fanControllerTable->fanLevel_f / fanControllerTable->temperature_c;
|
|
q = 0 - m;
|
|
|
|
fanLevelSet_f = (m * temperatureC_f) + q;
|
|
|
|
}else if(temperatureC_f >= (fanControllerTable + 9)->temperature_c)
|
|
{
|
|
fanLevelSet_f = (fanControllerTable + 9)->fanLevel_f;
|
|
}else
|
|
{
|
|
for(int i = 0; i < (TABLE_SIZE/sizeof(TemperaturePoint)) - 1; i++)
|
|
{
|
|
if(temperatureC_f >= (fanControllerTable + i)->temperature_c && temperatureC_f <= (fanControllerTable + i + 1)->temperature_c)
|
|
{
|
|
float m = 0;
|
|
float q = 0;
|
|
|
|
m = ((fanControllerTable + i + 1)->fanLevel_f - (fanControllerTable + i)->fanLevel_f ) / ((fanControllerTable + i + 1)->temperature_c - (fanControllerTable + i)->temperature_c);
|
|
q = (fanControllerTable + i)->fanLevel_f - (m * (fanControllerTable + i)->temperature_c);
|
|
|
|
fanLevelSet_f = (m * temperatureC_f) + q;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Always update fan speed for immediate response
|
|
rs = fanControllerSetRotationSpeedLevel(&fc, fanLevelSet_f);
|
|
if(R_FAILED(rs))
|
|
{
|
|
WriteLog("fanControllerSetRotationSpeedLevel error");
|
|
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
|
}
|
|
|
|
// Use responsive sleep time when awake (250ms)
|
|
svcSleepThread(awakeSleepTime);
|
|
}
|
|
|
|
fanControllerClose(&fc);
|
|
}
|
|
|
|
void StartFanControllerThread()
|
|
{
|
|
if(R_FAILED(threadStart(&FanControllerThread)))
|
|
{
|
|
WriteLog("Error starting FanControllerThread");
|
|
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
|
}
|
|
}
|
|
|
|
void CloseFanControllerThread()
|
|
{
|
|
Result rs;
|
|
fanControllerThreadExit = true;
|
|
rs = threadWaitForExit(&FanControllerThread);
|
|
if(R_FAILED(rs))
|
|
{
|
|
WriteLog("Error waiting fanControllerThread");
|
|
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
|
}
|
|
threadClose(&FanControllerThread);
|
|
fanControllerThreadExit = false;
|
|
free(fanControllerTable);
|
|
}
|
|
|
|
void WaitFanController()
|
|
{
|
|
if(R_FAILED(threadWaitForExit(&FanControllerThread)))
|
|
{
|
|
WriteLog("Error waiting fanControllerThread");
|
|
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen));
|
|
}
|
|
} |