diff --git a/nx/include/switch.h b/nx/include/switch.h index 3d457de0..d7b52ba3 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -58,6 +58,7 @@ extern "C" { #include "switch/services/gpio.h" #include "switch/services/bpc.h" #include "switch/services/pcv.h" +#include "switch/services/clkrst.h" #include "switch/services/psm.h" #include "switch/services/spsm.h" //#include "switch/services/bsd.h" Use switch/runtime/devices/socket.h instead diff --git a/nx/include/switch/services/clkrst.h b/nx/include/switch/services/clkrst.h new file mode 100644 index 00000000..6bd77eff --- /dev/null +++ b/nx/include/switch/services/clkrst.h @@ -0,0 +1,21 @@ +/** + * @file clkrst.h + * @brief Clkrst service IPC wrapper. + * @author p-sam + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../services/sm.h" +#include "../services/pcv.h" + +typedef struct { + Service s; +} ClkrstSession; + +Result clkrstInitialize(void); +void clkrstExit(void); +Result clkrstOpenSession(ClkrstSession* session_out, PcvModule module); +void clkrstCloseSession(ClkrstSession* session); +Result clkrstSetClockRate(ClkrstSession* session, u32 hz); +Result clkrstGetClockRate(ClkrstSession* session, u32 *out_hz); diff --git a/nx/source/services/clkrst.c b/nx/source/services/clkrst.c new file mode 100644 index 00000000..8857e173 --- /dev/null +++ b/nx/source/services/clkrst.c @@ -0,0 +1,152 @@ +#include "types.h" +#include "result.h" +#include "arm/atomics.h" +#include "kernel/ipc.h" +#include "services/pcv.h" +#include "runtime/hosversion.h" +#include "services/sm.h" +#include "services/clkrst.h" + +static Service g_clkrstSrv; +static u64 g_refCnt; + +Result clkrstInitialize(void) { + if(hosversionBefore(8,0,0)) { + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + } + + atomicIncrement64(&g_refCnt); + + if (serviceIsActive(&g_clkrstSrv)) { + return 0; + } + + Result rc = smGetService(&g_clkrstSrv, "clkrst"); + + if (R_FAILED(rc)) { + clkrstExit(); + } + + return rc; +} + + +void clkrstExit(void) { + if (atomicDecrement64(&g_refCnt) == 0) { + serviceClose(&g_clkrstSrv); + } +} + +Result clkrstOpenSession(ClkrstSession* session_out, PcvModule module) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 module; + u32 unk; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + raw->module = 0x40000001 + module; + raw->unk = 3; + + Result rc = serviceIpcDispatch(&g_clkrstSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + if (R_SUCCEEDED(rc)) { + serviceCreate(&session_out->s, r.Handles[0]); + } + } + + return rc; +} + + +void clkrstCloseSession(ClkrstSession* session) { + serviceClose(&session->s); +} + +Result clkrstSetClockRate(ClkrstSession* session, u32 hz) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 hz; + } *raw; + + raw = serviceIpcPrepareHeader(&session->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 7; + raw->hz = hz; + + Result rc = serviceIpcDispatch(&session->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&session->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result clkrstGetClockRate(ClkrstSession* session, u32 *out_hz) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&session->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 8; + + Result rc = serviceIpcDispatch(&session->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 hz; + } *resp; + + serviceIpcParse(&session->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + *out_hz = resp->hz; + } + } + + return rc; +}