From e12d3f042db1d1708b383ecd6c404aa9c20aef7f Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Sun, 17 May 2020 01:53:18 +0100 Subject: [PATCH] Add sleep state FSM handling service commands --- libctru/include/3ds/services/apt.h | 9 +++ libctru/include/3ds/services/ptmsysm.h | 78 ++++++++++++++++++++++++-- libctru/source/services/apt.c | 10 ++++ libctru/source/services/ptmsysm.c | 71 ++++++++++++++++++++++- 4 files changed, 163 insertions(+), 5 deletions(-) diff --git a/libctru/include/3ds/services/apt.h b/libctru/include/3ds/services/apt.h index 32374ae..46a04e0 100644 --- a/libctru/include/3ds/services/apt.h +++ b/libctru/include/3ds/services/apt.h @@ -45,6 +45,8 @@ typedef enum { typedef u8 APT_AppletAttr; +struct PtmWakeEvents; + /// Create an APT_AppletAttr bitfield from its components. static inline APT_AppletAttr aptMakeAppletAttr(APT_AppletPos pos, bool manualGpuRights, bool manualDspRights) { @@ -344,6 +346,13 @@ Result APT_IsRegistered(NS_APPID appID, bool* out); */ Result APT_InquireNotification(u32 appID, APT_Signal* signalType); +/** + * @brief Requests to enter sleep mode, and later sets wake events if allowed to. + * @param wakeEvents The wake events. Limited to "shell" (bit 1) for the PDN wake events part + * and "shell opened", "shell closed" and "HOME button pressed" for the MCU interrupts part. + */ +Result APT_SleepSystem(const struct PtmWakeEvents *wakeEvents); + /** * @brief Notifies an application to wait. * @param appID ID of the application. diff --git a/libctru/include/3ds/services/ptmsysm.h b/libctru/include/3ds/services/ptmsysm.h index 0978bb6..f39af46 100644 --- a/libctru/include/3ds/services/ptmsysm.h +++ b/libctru/include/3ds/services/ptmsysm.h @@ -4,15 +4,85 @@ */ #pragma once +#include <3ds/types.h> + +/// PDN wake events and MCU interrupts to select, combined with those of other processes +typedef struct PtmWakeEvents { + u32 pdn_wake_events; ///< Written to PDN_WAKE_EVENTS. Don't select bit26 (MCU), PTM will do it automatically. + u32 mcu_interupt_mask; ///< MCU interrupts to check when a MCU wake event happens. +} PtmWakeEvents; + +typedef struct { + PtmWakeEvents exit_sleep_events; ///< Wake events for which the system should fully wake up. + PtmWakeEvents continue_sleep_events; ///< Wake events for which the system should return to sleep. +} PtmSleepConfig; + +enum { + // Sleep FSM notification IDs + PTMNOTIFID_SLEEP_REQUESTED = 0x101, ///< @ref PTMSYSM_RequestSleep has been called (ack = 3) + PTMNOTIFID_SLEEP_DENIED = 0x102, ///< The sleep request has been denied by @ref PTMSYSM_ReplyToSleepQuery(true) (no ack required). + PTMNOTIFID_SLEEP_ALLOWED = 0x103, ///< The sleep request has been allowed by @ref PTMSYSM_ReplyToSleepQuery(false) (ack = 1). + PTMNOTIFID_GOING_TO_SLEEP = 0x104, ///< All processes not having "RunnableOnSleep" have been paused & the system is about to go to sleep (ack = 0). + PTMNOTIFID_FULLY_WAKING_UP = 0x105, ///< The system has been woken up, and the paused processes are about to be unpaused (ack = 1). + PTMNOTIFID_FULLY_AWAKE = 0x106, ///< The system is fully awake (no ack required). + PTMNOTIFID_HALF_AWAKE = 0x107, ///< The system has been woken up but is about to go to sleep again (ack = 2). + + PTMNOTIFID_SHUTDOWN = 0x108, ///< The system is about to power off or reboot. + + PTMNOTIFID_BATTERY_VERY_LOW = 0x211, ///< The battery level has reached 5% or below. + PTMNOTIFID_BATTERY_LOW = 0x212, ///< The battery level has reached 10% or below. +}; + +/// See @ref PTMSYSM_NotifySleepPreparationComplete. Corresponds to the number of potentially remaning notifs. until sleep/wakeup. +static inline s32 ptmSysmGetNotificationAckValue(u32 id) +{ + static const s32 values[] = { 3, -1, 1, 0, 0, -1, 2 }; + if (id < PTMNOTIFID_SLEEP_REQUESTED || id > PTMNOTIFID_HALF_AWAKE) + return -1; + return values[id - PTMNOTIFID_SLEEP_REQUESTED]; +} + /// Initializes ptm:sysm. Result ptmSysmInit(void); /// Exits ptm:sysm. void ptmSysmExit(void); +/// Requests to enter sleep mode. +Result PTMSYSM_RequestSleep(void); /** - * @brief return 1 if it's a New 3DS otherwise, return 0 for Old 3DS. + * @brief Accepts or denies the incoming sleep mode request. + * @param deny Whether or not to deny the sleep request. + * @note If deny = false, this is equivalent to calling @ref PTMSYSM_NotifySleepPreparationComplete(3) + */ +Result PTMSYSM_ReplyToSleepQuery(bool deny); + +/** + * @brief Acknowledges the current sleep notification and advance the internal sleep mode FSM. All subscribers must reply. + * @param ackValue Use @ref ptmSysmGetNotificationAckValue + * @note @ref PTMNOTIFID_SLEEP_DENIED and @ref PTMNOTIFID_FULLY_AWAKE don't require this. + */ +Result PTMSYSM_NotifySleepPreparationComplete(s32 ackValue); + +/** + * @brief Sets the wake events (two sets: when to fully wake up and when to return to sleep). + * @param sleepConfig Pointer to the two sets of wake events. + * @note Can only be called just before acknowledging @ref PTMNOTIFID_GOING_TO_SLEEP or @ref PTMNOTIFID_HALF_AWAKE. + */ +Result PTMSYSM_SetWakeEvents(const PtmSleepConfig *sleepConfig); + +/** + * @brief Gets the wake reason (only the first applicable wake event is taken into account). + * @param sleepConfig Pointer to the two sets of wake events. Only the relevant set will be filled. + */ +Result PTMSYSM_GetWakeReason(PtmSleepConfig *outSleepConfig); + +/// Cancels the "half-awake" state and fully wakes up the 3DS after some delay. +Result PTMSYSM_Awaken(void); + +/** + * @brief Returns 1 if it's a New 3DS, otherwise 0. */ Result PTMSYSM_CheckNew3DS(void); @@ -23,13 +93,13 @@ Result PTMSYSM_CheckNew3DS(void); Result PTMSYSM_ConfigureNew3DSCPU(u8 value); /** - * @brief Trigger a hardware system shutdown via the MCU - * @param timeout: timeout passed to PMApp:TerminateNonEssential. + * @brief Trigger a hardware system shutdown via the MCU. + * @param timeout: timeout passed to PMApp:ShutdownAsync (PrepareForReboot). */ Result PTMSYSM_ShutdownAsync(u64 timeout); /** * @brief Trigger a hardware system reboot via the MCU. - * @param timeout: timeout passed to PMApp:TerminateNonEssential. + * @param timeout: timeout passed to PMApp:ShutdownAsync (PrepareForReboot). */ Result PTMSYSM_RebootAsync(u64 timeout); diff --git a/libctru/source/services/apt.c b/libctru/source/services/apt.c index a07b94b..5b31c98 100644 --- a/libctru/source/services/apt.c +++ b/libctru/source/services/apt.c @@ -11,6 +11,7 @@ #include <3ds/synchronization.h> #include <3ds/services/apt.h> #include <3ds/services/gspgpu.h> +#include <3ds/services/ptmsysm.h> // for PtmWakeEvents #include <3ds/ipc.h> #include <3ds/env.h> #include <3ds/thread.h> @@ -813,6 +814,15 @@ Result APT_JumpToApplication(const void* param, size_t paramSize, Handle handle) return aptSendCommand(cmdbuf); } +Result APT_SleepSystem(const PtmWakeEvents *wakeEvents) +{ + u32 cmdbuf[16]; + cmdbuf[0]=IPC_MakeHeader(0x42, 2, 0); + memcpy(&cmdbuf[1], wakeEvents, sizeof(PtmWakeEvents)); + + return aptSendCommand(cmdbuf); +} + Result APT_NotifyToWait(NS_APPID appID) { u32 cmdbuf[16]; diff --git a/libctru/source/services/ptmsysm.c b/libctru/source/services/ptmsysm.c index 6ee8da4..de9d2ad 100644 --- a/libctru/source/services/ptmsysm.c +++ b/libctru/source/services/ptmsysm.c @@ -1,4 +1,4 @@ -#include +#include #include <3ds/types.h> #include <3ds/result.h> #include <3ds/svc.h> @@ -24,6 +24,75 @@ void ptmSysmExit(void) svcCloseHandle(ptmSysmHandle); } +Result PTMSYSM_RequestSleep(void) +{ + Result ret; + u32 *cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x0406,0,0); // 0x04060000 + + if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return ret; + + return (Result)cmdbuf[1]; +} + +Result PTMSYSM_ReplyToSleepQuery(bool deny) +{ + Result ret; + u32 *cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x0402,1,2); // 0x04020042 + cmdbuf[1] = (u32)deny; + cmdbuf[2] = IPC_Desc_CurProcessId(); + if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return ret; + + return (Result)cmdbuf[1]; +} + +Result PTMSYSM_NotifySleepPreparationComplete(s32 ackValue) +{ + Result ret; + u32 *cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x0403,1,2); // 0x04030042 + cmdbuf[1] = (u32)ackValue; + cmdbuf[2] = IPC_Desc_CurProcessId(); + if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return ret; + + return (Result)cmdbuf[1]; +} + +Result PTMSYSM_SetWakeEvents(const PtmSleepConfig *sleepConfig) +{ + Result ret; + u32 *cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x0404,4,2); // 0x04040102 + memcpy(&cmdbuf[1], sleepConfig, sizeof(PtmSleepConfig)); + cmdbuf[5] = IPC_Desc_CurProcessId(); + if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return ret; + + return (Result)cmdbuf[1]; +} + +Result PTMSYSM_GetWakeReason(PtmSleepConfig *outSleepConfig) +{ + Result ret; + u32 *cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x0405,0,0); // 0x04050000 + if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return ret; + memcpy(outSleepConfig, &cmdbuf[2], sizeof(PtmSleepConfig)); + + return (Result)cmdbuf[1]; +} + +Result PTMSYSM_Awaken(void) +{ + Result ret; + u32 *cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x0408,0,0); // 0x04080000 + + if(R_FAILED(ret = svcSendSyncRequest(ptmSysmHandle)))return ret; + + return (Result)cmdbuf[1]; +} + Result PTMSYSM_CheckNew3DS(void) { Result ret;