diff --git a/libctru/include/3ds/services/dsp.h b/libctru/include/3ds/services/dsp.h index fcbab26..91dbf05 100644 --- a/libctru/include/3ds/services/dsp.h +++ b/libctru/include/3ds/services/dsp.h @@ -13,12 +13,23 @@ typedef enum DSP_INTERRUPT_PIPE = 2 ///< Pipe interrupt. } DSP_InterruptType; -/// DSP pipe directions. +/// DSP hook types. typedef enum { - DSP_PIPE_INPUT = 0, ///< DSP to ARM - DSP_PIPE_OUTPUT = 1 ///< ARM to DSP -} DSP_PipeDirection; + DSPHOOK_ONSLEEP = 0, ///< DSP is going to sleep. + DSPHOOK_ONWAKEUP = 1, ///< DSP is waking up. + DSPHOOK_ONCANCEL = 2, ///< DSP was sleeping and the app was cancelled. +} DSP_HookType; + +/// DSP hook function. +typedef void (* dspHookFn)(DSP_HookType hook); + +/// DSP hook cookie. +typedef struct tag_dspHookCookie +{ + struct tag_dspHookCookie* next; ///< Next cookie. + dspHookFn callback; ///< Hook callback. +} dspHookCookie; /** * @brief Initializes the dsp service. @@ -35,6 +46,22 @@ Result dspInit(void); */ void dspExit(void); +/// Returns true if a component is loaded, false otherwise. +bool dspIsComponentLoaded(void); + +/** + * @brief Sets up a DSP status hook. + * @param cookie Hook cookie to use. + * @param callback Function to call when DSP's status changes. + */ +void dspHook(dspHookCookie* cookie, dspHookFn callback); + +/** + * @brief Removes a DSP status hook. + * @param cookie Hook cookie to remove. + */ +void dspUnhook(dspHookCookie* cookie); + /** * @brief Checks if a headphone is inserted. * @param is_inserted Pointer to output the insertion status to. diff --git a/libctru/source/ndsp/ndsp.c b/libctru/source/ndsp/ndsp.c index ab6199e..dd82d72 100644 --- a/libctru/source/ndsp/ndsp.c +++ b/libctru/source/ndsp/ndsp.c @@ -9,7 +9,7 @@ u16 ndspFrameId, ndspBufferCurId, ndspBufferId; void* ndspVars[16][2]; -static bool bComponentLoaded = false, bDspReady = false, bSleeping = false, bActuallySleeping = false, bNeedsSync = false; +static bool bDspReady = false, bSleeping = false, bActuallySleeping = false, bNeedsSync = false; static u32 droppedFrames, frameCount; static const void* componentBin; @@ -17,7 +17,7 @@ static u32 componentSize; static u16 componentProgMask, componentDataMask; static bool componentFree; -static aptHookCookie aptCookie; +static dspHookCookie ndspHookCookie; static Handle irqEvent, dspSem; static LightEvent sleepEvent; @@ -28,12 +28,6 @@ static u8 dspVar5Backup[0x1080]; static volatile bool ndspThreadRun; static Thread ndspThread; -static Result ndspLoadComponent(void) -{ - if (!componentBin) return 1; - return DSP_LoadComponent(componentBin, componentSize, componentProgMask, componentDataMask, &bComponentLoaded); -} - static inline void ndspWaitForIrq(void) { LightLock_Lock(&ndspMutex); @@ -209,9 +203,6 @@ static Result ndspInitialize(bool resume) { Result rc; - rc = ndspLoadComponent(); - if (R_FAILED(rc)) return rc; - rc = svcCreateEvent(&irqEvent, RESET_STICKY); if (R_FAILED(rc)) goto _fail1; @@ -266,7 +257,6 @@ _fail3: _fail2: svcCloseHandle(irqEvent); _fail1: - DSP_UnloadComponent(); return rc; } @@ -292,20 +282,20 @@ static void ndspFinalize(bool suspend) DSP_RegisterInterruptEvents(0, 2, 2); svcCloseHandle(irqEvent); svcCloseHandle(dspSem); - DSP_UnloadComponent(); - bComponentLoaded = false; + bDspReady = false; LightLock_Unlock(&ndspMutex); } -static void ndspAptHook(APT_HookType hook, void* param) +static void ndspHookCallback(DSP_HookType hook) { switch (hook) { - case APTHOOK_ONRESTORE: - case APTHOOK_ONWAKEUP: + case DSPHOOK_ONWAKEUP: bSleeping = false; - ndspInitialize(true); + Result res = ndspInitialize(true); + if (R_FAILED(res)) + svcBreak(USERBREAK_PANIC); // Shouldn't happen. if (bActuallySleeping) { bActuallySleeping = false; @@ -313,8 +303,7 @@ static void ndspAptHook(APT_HookType hook, void* param) } break; - case APTHOOK_ONSUSPEND: - case APTHOOK_ONSLEEP: + case DSPHOOK_ONSLEEP: bSleeping = true; ndspFinalize(true); break; @@ -481,6 +470,9 @@ Result ndspInit(void) rc = dspInit(); if (R_FAILED(rc)) goto _fail1; + rc = DSP_LoadComponent(componentBin, componentSize, componentProgMask, componentDataMask, NULL); + if (R_FAILED(rc)) goto _fail2; + rc = ndspInitialize(false); if (R_FAILED(rc)) goto _fail2; @@ -489,7 +481,7 @@ Result ndspInit(void) ndspThread = threadCreate(ndspThreadMain, 0x0, NDSP_THREAD_STACK_SIZE, 0x18, -2, true); if (!ndspThread) goto _fail3; - aptHook(&aptCookie, ndspAptHook, NULL); + dspHook(&ndspHookCookie, ndspHookCallback); return 0; _fail3: @@ -518,7 +510,7 @@ void ndspExit(void) LightEvent_Signal(&sleepEvent); } threadJoin(ndspThread, U64_MAX); - aptUnhook(&aptCookie); + dspUnhook(&ndspHookCookie); if (!bSleeping) ndspFinalize(false); bSleeping = false; diff --git a/libctru/source/services/apt.c b/libctru/source/services/apt.c index cfd77b6..baf1517 100644 --- a/libctru/source/services/apt.c +++ b/libctru/source/services/apt.c @@ -55,6 +55,7 @@ enum FLAG_CANCELLED = BIT(10), // Miscellaneous + FLAG_DSPWAKEUP = BIT(29), FLAG_CHAINLOAD = BIT(30), FLAG_SPURIOUS = BIT(31), }; @@ -87,6 +88,11 @@ __attribute__((weak)) void _aptDebug(int a, int b) { } #define _aptDebug(a,b) ((void)0) #endif +// APT<->DSP interaction functions (stubbed when not using DSP) +__attribute__((weak)) bool aptDspSleep(void) { return false; } +__attribute__((weak)) void aptDspWakeup(void) { } +__attribute__((weak)) void aptDspCancel(void) { } + static void aptCallHook(APT_HookType hookType) { aptHookCookie* c; @@ -448,16 +454,34 @@ void aptEventHandler(void *arg) // - APTCMD_DSP_SLEEP (*NOT* cancelled afterwards) // - APTCMD_DSP_WAKEUP (*NOT* cancelled afterwards) - // We will instead only handle spurious APTCMD_WAKEUP_PAUSE parameters - // (see aptInit for more details on the hax 2.x spurious wakeup problem) - if (cmd == APTCMD_WAKEUP_PAUSE && (aptFlags & FLAG_SPURIOUS)) + // We will handle the following: + switch (cmd) { - APT_CancelParameter(APPID_NONE, envGetAptAppId(), NULL); - aptFlags &= ~FLAG_SPURIOUS; - continue; + case APTCMD_DSP_SLEEP: + // Handle DSP sleep requests + aptDspSleep(); + break; + case APTCMD_DSP_WAKEUP: + // Handle DSP wakeup requests + aptFlags &= ~FLAG_DSPWAKEUP; + aptDspWakeup(); + break; + case APTCMD_WAKEUP_PAUSE: + // Handle spurious APTCMD_WAKEUP_PAUSE parameters + // (see aptInit for more details on the hax 2.x spurious wakeup problem) + if (aptFlags & FLAG_SPURIOUS) + { + APT_CancelParameter(APPID_NONE, envGetAptAppId(), NULL); + aptFlags &= ~FLAG_SPURIOUS; + break; + } + // Fallthrough otherwise + default: + // Others not accounted for -> pass it on to aptReceiveParameter + LightEvent_Signal(&aptReceiveEvent); + break; } - LightEvent_Signal(&aptReceiveEvent); continue; } @@ -505,6 +529,8 @@ void aptEventHandler(void *arg) break; case APTSIGNAL_SLEEP_ENTER: _aptDebug(10, aptFlags); + if (aptDspSleep()) + aptFlags |= FLAG_DSPWAKEUP; if (aptIsActive()) aptFlags |= FLAG_SHOULDSLEEP; else @@ -512,6 +538,11 @@ void aptEventHandler(void *arg) APT_ReplySleepNotificationComplete(envGetAptAppId()); break; case APTSIGNAL_SLEEP_WAKEUP: + if (aptFlags & FLAG_DSPWAKEUP) + { + aptFlags &= ~FLAG_DSPWAKEUP; + aptDspWakeup(); + } if (!aptIsActive()) break; if (aptFlags & FLAG_SLEEPING) @@ -589,8 +620,16 @@ APT_Command aptWaitForWakeUp(APT_Transition transition) aptCallHook(APTHOOK_ONRESTORE); } - if (cmd == APTCMD_WAKEUP_CANCEL) - aptFlags |= FLAG_CANCELLED; + if (cmd == APTCMD_WAKEUP_CANCEL || cmd == APTCMD_WAKEUP_CANCELALL) + { + aptDspCancel(); + if (cmd == APTCMD_WAKEUP_CANCEL) // for some reason, not for CANCELALL... is this a bug in official sw? + aptFlags |= FLAG_CANCELLED; + } else if (cmd != APTCMD_WAKEUP_LAUNCHAPP) + { + aptFlags &= ~FLAG_DSPWAKEUP; + aptDspWakeup(); + } if (cmd != APTCMD_WAKEUP_JUMPTOHOME) { @@ -732,6 +771,7 @@ void aptJumpToHomeMenu(void) GSPGPU_SaveVramSysArea(); aptScreenTransfer(aptGetMenuAppID(), true); + aptDspSleep(); GSPGPU_ReleaseRight(); APT_JumpToHomeMenu(NULL, 0, 0); diff --git a/libctru/source/services/dsp.c b/libctru/source/services/dsp.c index 3594f32..0253d69 100644 --- a/libctru/source/services/dsp.c +++ b/libctru/source/services/dsp.c @@ -9,6 +9,20 @@ static Handle dspHandle; static int dspRefCount; +static dspHookCookie dspFirstHook; +static bool dspComponentLoaded, dspSleeping; + +static const void* dspSavedCompBin; +static u32 dspSavedCompSize; +static u16 dspSavedCompProgMask, dspSavedCompDataMask; + +static void dspCallHook(DSP_HookType hookType) +{ + dspHookCookie* c; + for (c = &dspFirstHook; c && c->callback; c = c->next) + c->callback(hookType); +} + Result dspInit(void) { Result ret = 0; @@ -16,18 +30,91 @@ Result dspInit(void) if (AtomicPostIncrement(&dspRefCount)) return 0; ret = srvGetServiceHandle(&dspHandle, "dsp::DSP"); - if (R_SUCCEEDED(ret)) DSP_UnloadComponent(); - else AtomicDecrement(&dspRefCount); + if (R_FAILED(ret)) + { + AtomicDecrement(&dspRefCount); + return ret; + } - return ret; + // Force unload component (if exists) + dspComponentLoaded = true; + DSP_UnloadComponent(); + + dspSleeping = false; + dspSavedCompBin = NULL; + dspSavedCompSize = 0; + dspSavedCompProgMask = 0; + dspSavedCompDataMask = 0; + return 0; } void dspExit(void) { if (AtomicDecrement(&dspRefCount)) return; + DSP_UnloadComponent(); svcCloseHandle(dspHandle); } +bool dspIsComponentLoaded(void) +{ + return dspComponentLoaded; +} + +void dspHook(dspHookCookie* cookie, dspHookFn callback) +{ + if (!callback) return; + + dspHookCookie* hook = &dspFirstHook; + *cookie = *hook; // Structure copy. + hook->next = cookie; + hook->callback = callback; +} + +void dspUnhook(dspHookCookie* cookie) +{ + dspHookCookie* hook; + for (hook = &dspFirstHook; hook; hook = hook->next) + { + if (hook->next == cookie) + { + *hook = *cookie; // Structure copy. + break; + } + } +} + +bool aptDspSleep(void) +{ + if (!dspComponentLoaded || dspSleeping) + return false; + + dspCallHook(DSPHOOK_ONSLEEP); + DSP_UnloadComponent(); + dspSleeping = true; + return true; +} + +void aptDspWakeup(void) +{ + if (!dspSleeping) + return; + + Result ret = DSP_LoadComponent(dspSavedCompBin, dspSavedCompSize, dspSavedCompProgMask, dspSavedCompDataMask, NULL); + if (R_FAILED(ret)) + svcBreak(USERBREAK_PANIC); // Shouldn't happen. + + dspCallHook(DSPHOOK_ONWAKEUP); + dspSleeping = false; +} + +void aptDspCancel(void) +{ + if (!dspSleeping) + return; + + dspCallHook(DSPHOOK_ONCANCEL); +} + Result DSP_GetHeadphoneStatus(bool* is_inserted) { Result ret = 0; @@ -96,6 +183,17 @@ Result DSP_GetSemaphoreHandle(Handle* semaphore) Result DSP_LoadComponent(const void* component, u32 size, u16 prog_mask, u16 data_mask, bool* is_loaded) { + if (dspComponentLoaded) + { + if (is_loaded) *is_loaded = dspComponentLoaded; + return 0; + } + + dspSavedCompBin = component; + dspSavedCompSize = size; + dspSavedCompProgMask = prog_mask; + dspSavedCompDataMask = data_mask; + Result ret = 0; u32* cmdbuf = getThreadCommandBuffer(); cmdbuf[0] = IPC_MakeHeader(0x11,3,2); @@ -105,12 +203,18 @@ Result DSP_LoadComponent(const void* component, u32 size, u16 prog_mask, u16 dat cmdbuf[4] = IPC_Desc_Buffer(size,IPC_BUFFER_R); cmdbuf[5] = (u32) component; if (R_FAILED(ret = svcSendSyncRequest(dspHandle))) return ret; - *is_loaded = cmdbuf[2] & 0xFF; + dspComponentLoaded = cmdbuf[2] & 0xFF; + if (is_loaded) *is_loaded = dspComponentLoaded; return cmdbuf[1]; } Result DSP_UnloadComponent(void) { + if (!dspComponentLoaded) + return 0; + + dspComponentLoaded = false; + Result ret = 0; u32* cmdbuf = getThreadCommandBuffer(); cmdbuf[0] = IPC_MakeHeader(0x12,0,0);