/* * Copyright (c) Souldbminer and Horizon OC Contributors * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * , , * 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 #include #include "file_utils.h" #include "errors.h" #include "clock_manager.h" IpcService::IpcService(ClockManager* clockMgr) { 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; this->clockMgr = clockMgr; } 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) && r->hipc.meta.num_recv_buffers >= 1) { size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers); if(bufSize >= sizeof(SysClkTitleProfileList)) { return ipcSrv->GetProfiles((std::uint64_t*)r->data.ptr, (SysClkTitleProfileList*)hipcGetBufferAddress(r->hipc.data.recv_buffers)); } } 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: if(r->hipc.meta.num_recv_buffers >= 1) { size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers); if(bufSize >= sizeof(SysClkConfigValueList)) { return ipcSrv->GetConfigValues((SysClkConfigValueList*)hipcGetBufferAddress(r->hipc.data.recv_buffers)); } } break; case SysClkIpcCmd_SetConfigValues: if(r->hipc.meta.num_send_buffers >= 1) { size_t bufSize = hipcGetBufferSize(r->hipc.data.send_buffers); if(bufSize >= sizeof(SysClkConfigValueList)) { return ipcSrv->SetConfigValues((SysClkConfigValueList*)hipcGetBufferAddress(r->hipc.data.send_buffers)); } } break; case SysClkIpcCmd_GetFreqList: if(r->data.size >= sizeof(SysClkIpc_GetFreqList_Args) && r->hipc.meta.num_recv_buffers >= 1) { *out_dataSize = sizeof(std::uint32_t); return ipcSrv->GetFreqList( (SysClkIpc_GetFreqList_Args*)r->data.ptr, (std::uint32_t*)hipcGetBufferAddress(r->hipc.data.recv_buffers), hipcGetBufferSize(r->hipc.data.recv_buffers), (std::uint32_t*)out_data ); } break; case SysClkIpcCmd_SetReverseNXRTMode: if (r->data.size >= sizeof(ReverseNXMode)) { ReverseNXMode mode = *((ReverseNXMode*)r->data.ptr); return ipcSrv->SetReverseNXRTMode(mode); } break; case HocClkIpcCmd_SetKipData: return ipcSrv->SetKipData(); } 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) { *out_ctx = this->clockMgr->GetCurrentContext(); return 0; } Result IpcService::Exit() { this->clockMgr->SetRunning(false); return 0; } Result IpcService::GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count) { Config* config = this->clockMgr->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 = this->clockMgr->GetConfig(); if(!config->HasProfilesLoaded()) { return SYSCLK_ERROR(ConfigNotLoaded); } config->GetProfiles(*tid, out_profiles); return 0; } Result IpcService::SetProfiles(SysClkIpc_SetProfiles_Args* args) { Config* config = this->clockMgr->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 = this->clockMgr->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 = this->clockMgr->GetConfig(); config->SetOverrideHz(module, hz); return 0; } Result IpcService::GetConfigValues(SysClkConfigValueList* out_configValues) { Config* config = this->clockMgr->GetConfig(); if(!config->HasProfilesLoaded()) { return SYSCLK_ERROR(ConfigNotLoaded); } config->GetConfigValues(out_configValues); return 0; } Result IpcService::SetConfigValues(SysClkConfigValueList* configValues) { Config* config = this->clockMgr->GetConfig(); if(!config->HasProfilesLoaded()) { return SYSCLK_ERROR(ConfigNotLoaded); } SysClkConfigValueList configValuesCopy = *configValues; if(!config->SetConfigValues(&configValuesCopy, true)) { return SYSCLK_ERROR(ConfigSaveFailed); } return 0; } Result IpcService::GetFreqList(SysClkIpc_GetFreqList_Args* args, std::uint32_t* out_list, std::size_t size, std::uint32_t* out_count) { if(!SYSCLK_ENUM_VALID(SysClkModule, args->module)) { return SYSCLK_ERROR(Generic); } if(args->maxCount != size/sizeof(*out_list)) { return SYSCLK_ERROR(Generic); } this->clockMgr->GetFreqList(args->module, out_list, args->maxCount, out_count); return 0; } Result IpcService::SetReverseNXRTMode(ReverseNXMode mode) { ClockManager::GetInstance()->SetRNXRTMode(mode); return 0; } Result IpcService::SetKipData() { ClockManager::GetInstance()->SetKipData(); return 0; } Result IpcService::GetKipData() { ClockManager::GetInstance()->GetKipConfigValues(); return 0; }