diff --git a/nx/include/switch.h b/nx/include/switch.h index 353c83b0..1085a573 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -68,6 +68,7 @@ extern "C" { #include "switch/services/lr.h" #include "switch/services/spl.h" #include "switch/services/ncm.h" +#include "switch/services/psc.h" #include "switch/display/gfx.h" #include "switch/display/binder.h" diff --git a/nx/include/switch/services/psc.h b/nx/include/switch/services/psc.h new file mode 100644 index 00000000..d0e457d0 --- /dev/null +++ b/nx/include/switch/services/psc.h @@ -0,0 +1,34 @@ +/** + * @file psc.h + * @brief PSC service IPC wrapper. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../kernel/event.h" +#include "../services/sm.h" + +typedef enum { + PscPmState_Awake = 0, ///< Everything is awake. + PscPmState_ReadyAwaken = 1, ///< Preparing to transition to awake. + PscPmState_ReadySleep = 2, ///< Preparing to transition to sleep. + PscPmState_ReadySleepCritical = 3, ///< Critical services are ready to sleep. + PscPmState_ReadyAwakenCritical = 4, ///< Critical services are ready to wake up. + PscPmState_ReadyShutdown = 5, ///< Preparing to transition to shutdown. +} PscPmState; + +typedef struct { + Event event; + Service srv; + u16 module_id; +} PscPmModule; + +Result pscInitialize(void); +void pscExit(void); + +Result pscGetPmModule(PscPmModule *out, u16 module_id, const u16 *dependencies, size_t dependency_count, bool autoclear); + +Result pscPmModuleGetRequest(PscPmModule *module, PscPmState *out_state, u32 *out_flags); +Result pscPmModuleAcknowledge(PscPmModule *module, PscPmState state); +Result pscPmModuleFinalize(PscPmModule *module); diff --git a/nx/source/services/psc.c b/nx/source/services/psc.c new file mode 100644 index 00000000..559f73bf --- /dev/null +++ b/nx/source/services/psc.c @@ -0,0 +1,272 @@ +/** + * @file psc.h + * @brief PSC service IPC wrapper. + * @author SciresM + * @copyright libnx Authors + */ +#include "types.h" +#include "result.h" +#include "arm/atomics.h" +#include "kernel/ipc.h" +#include "kernel/detect.h" +#include "kernel/event.h" +#include "services/psc.h" +#include "services/sm.h" + +static Service g_pscSrv; +static u64 g_refCnt; + +static Result _pscPmModuleInitialize(PscPmModule *module, u16 module_id, const u16 *dependencies, size_t dependency_count, bool autoclear); +static Result _pscPmModuleAcknowledge(PscPmModule *module); +static Result _pscPmModuleAcknowledgeEx(PscPmModule *module, PscPmState state); + +Result pscInitialize(void) { + Result rc = 0; + + atomicIncrement64(&g_refCnt); + + if (serviceIsActive(&g_pscSrv)) + return rc; + + rc = smGetService(&g_pscSrv, "psc:m"); + + if (R_SUCCEEDED(rc)) { + rc = serviceConvertToDomain(&g_pscSrv); + } + + if (R_FAILED(rc)) pscExit(); + + return rc; +} + +void pscExit(void) { + if (atomicDecrement64(&g_refCnt) == 0) { + serviceClose(&g_pscSrv); + } +} + +Result pscGetPmModule(PscPmModule *out, u16 module_id, const u16 *dependencies, size_t dependency_count, bool autoclear) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_pscSrv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + + Result rc = serviceIpcDispatch(&g_pscSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_pscSrv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreateSubservice(&out->srv, &g_pscSrv, &r, 0); + + rc = _pscPmModuleInitialize(out, module_id, dependencies, dependency_count, autoclear); + if (R_FAILED(rc)) { + serviceClose(&out->srv); + } + } + } + + return rc; +} + +Result _pscPmModuleInitialize(PscPmModule *module, u16 module_id, const u16 *dependencies, size_t dependency_count, bool autoclear) { + IpcCommand c; + ipcInitialize(&c); + ipcAddSendBuffer(&c, dependencies, dependency_count * sizeof(u16), BufferType_Normal); + + struct { + u64 magic; + u64 cmd_id; + u32 module_id; + } *raw; + + raw = serviceIpcPrepareHeader(&module->srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + raw->module_id = module_id; + + Result rc = serviceIpcDispatch(&module->srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&module->srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + eventLoadRemote(&module->event, r.Handles[0], autoclear); + module->module_id = module_id; + } + } + + return rc; +} + +Result pscPmModuleGetRequest(PscPmModule *module, PscPmState *out_state, u32 *out_flags) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&module->srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 1; + + Result rc = serviceIpcDispatch(&module->srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 state; + u32 flags; + } *resp; + + serviceIpcParse(&module->srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + *out_state = (PscPmState)resp->state; + *out_flags = resp->flags; + } + } + + return rc; +} + +Result pscPmModuleAcknowledge(PscPmModule *module, PscPmState state) { + if (kernelAbove600()) { + return _pscPmModuleAcknowledgeEx(module, state); + } else { + return _pscPmModuleAcknowledge(module); + } +} + +Result pscPmModuleFinalize(PscPmModule *module) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&module->srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 3; + + Result rc = serviceIpcDispatch(&module->srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&module->srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _pscPmModuleAcknowledge(PscPmModule *module) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&module->srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 2; + + Result rc = serviceIpcDispatch(&module->srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&module->srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _pscPmModuleAcknowledgeEx(PscPmModule *module, PscPmState state) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 state; + } *raw; + + raw = serviceIpcPrepareHeader(&module->srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 4; + raw->state = state; + + Result rc = serviceIpcDispatch(&module->srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&module->srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +}