Add sleep state FSM handling service commands

This commit is contained in:
TuxSH 2020-05-17 01:53:18 +01:00 committed by fincs
parent 1bafb08f34
commit e3be4b8678
4 changed files with 163 additions and 5 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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];

View File

@ -1,4 +1,4 @@
#include <stdlib.h>
#include <string.h>
#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;