Refactor DSP<->APT interaction, see details:

- DSP wrapper is now in charge of handling sleep/awake/cancel events
  - Added DSP hook mechanism (similar to APT hook)
  - The DSP component now gets automatically unloaded/reloaded as needed
- NDSP now uses DSP hook instead of APT hook to manage lifetime
  - Moved sleep/wakeup component management logic into NDSP
- APT now calls into DSP wrapper in order to arbitrate access to the DSP
  - Launching libapplets no longer relinquishes DSP rights - this means
    music can now be played in the background during libapplet activity
This commit is contained in:
fincs 2020-06-22 00:34:38 +02:00
parent b559d93eda
commit b93e7f19bf
No known key found for this signature in database
GPG Key ID: 62C7609ADA219C60
4 changed files with 202 additions and 39 deletions

View File

@ -13,12 +13,23 @@ typedef enum
DSP_INTERRUPT_PIPE = 2 ///< Pipe interrupt. DSP_INTERRUPT_PIPE = 2 ///< Pipe interrupt.
} DSP_InterruptType; } DSP_InterruptType;
/// DSP pipe directions. /// DSP hook types.
typedef enum typedef enum
{ {
DSP_PIPE_INPUT = 0, ///< DSP to ARM DSPHOOK_ONSLEEP = 0, ///< DSP is going to sleep.
DSP_PIPE_OUTPUT = 1 ///< ARM to DSP DSPHOOK_ONWAKEUP = 1, ///< DSP is waking up.
} DSP_PipeDirection; 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. * @brief Initializes the dsp service.
@ -35,6 +46,22 @@ Result dspInit(void);
*/ */
void dspExit(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. * @brief Checks if a headphone is inserted.
* @param is_inserted Pointer to output the insertion status to. * @param is_inserted Pointer to output the insertion status to.

View File

@ -9,7 +9,7 @@
u16 ndspFrameId, ndspBufferCurId, ndspBufferId; u16 ndspFrameId, ndspBufferCurId, ndspBufferId;
void* ndspVars[16][2]; 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 u32 droppedFrames, frameCount;
static const void* componentBin; static const void* componentBin;
@ -17,7 +17,7 @@ static u32 componentSize;
static u16 componentProgMask, componentDataMask; static u16 componentProgMask, componentDataMask;
static bool componentFree; static bool componentFree;
static aptHookCookie aptCookie; static dspHookCookie ndspHookCookie;
static Handle irqEvent, dspSem; static Handle irqEvent, dspSem;
static LightEvent sleepEvent; static LightEvent sleepEvent;
@ -28,12 +28,6 @@ static u8 dspVar5Backup[0x1080];
static volatile bool ndspThreadRun; static volatile bool ndspThreadRun;
static Thread ndspThread; static Thread ndspThread;
static Result ndspLoadComponent(void)
{
if (!componentBin) return 1;
return DSP_LoadComponent(componentBin, componentSize, componentProgMask, componentDataMask, &bComponentLoaded);
}
static inline void ndspWaitForIrq(void) static inline void ndspWaitForIrq(void)
{ {
LightLock_Lock(&ndspMutex); LightLock_Lock(&ndspMutex);
@ -209,9 +203,6 @@ static Result ndspInitialize(bool resume)
{ {
Result rc; Result rc;
rc = ndspLoadComponent();
if (R_FAILED(rc)) return rc;
rc = svcCreateEvent(&irqEvent, RESET_STICKY); rc = svcCreateEvent(&irqEvent, RESET_STICKY);
if (R_FAILED(rc)) goto _fail1; if (R_FAILED(rc)) goto _fail1;
@ -266,7 +257,6 @@ _fail3:
_fail2: _fail2:
svcCloseHandle(irqEvent); svcCloseHandle(irqEvent);
_fail1: _fail1:
DSP_UnloadComponent();
return rc; return rc;
} }
@ -292,20 +282,20 @@ static void ndspFinalize(bool suspend)
DSP_RegisterInterruptEvents(0, 2, 2); DSP_RegisterInterruptEvents(0, 2, 2);
svcCloseHandle(irqEvent); svcCloseHandle(irqEvent);
svcCloseHandle(dspSem); svcCloseHandle(dspSem);
DSP_UnloadComponent();
bComponentLoaded = false;
bDspReady = false; bDspReady = false;
LightLock_Unlock(&ndspMutex); LightLock_Unlock(&ndspMutex);
} }
static void ndspAptHook(APT_HookType hook, void* param) static void ndspHookCallback(DSP_HookType hook)
{ {
switch (hook) switch (hook)
{ {
case APTHOOK_ONRESTORE: case DSPHOOK_ONWAKEUP:
case APTHOOK_ONWAKEUP:
bSleeping = false; bSleeping = false;
ndspInitialize(true); Result res = ndspInitialize(true);
if (R_FAILED(res))
svcBreak(USERBREAK_PANIC); // Shouldn't happen.
if (bActuallySleeping) if (bActuallySleeping)
{ {
bActuallySleeping = false; bActuallySleeping = false;
@ -313,8 +303,7 @@ static void ndspAptHook(APT_HookType hook, void* param)
} }
break; break;
case APTHOOK_ONSUSPEND: case DSPHOOK_ONSLEEP:
case APTHOOK_ONSLEEP:
bSleeping = true; bSleeping = true;
ndspFinalize(true); ndspFinalize(true);
break; break;
@ -481,6 +470,9 @@ Result ndspInit(void)
rc = dspInit(); rc = dspInit();
if (R_FAILED(rc)) goto _fail1; if (R_FAILED(rc)) goto _fail1;
rc = DSP_LoadComponent(componentBin, componentSize, componentProgMask, componentDataMask, NULL);
if (R_FAILED(rc)) goto _fail2;
rc = ndspInitialize(false); rc = ndspInitialize(false);
if (R_FAILED(rc)) goto _fail2; if (R_FAILED(rc)) goto _fail2;
@ -489,7 +481,7 @@ Result ndspInit(void)
ndspThread = threadCreate(ndspThreadMain, 0x0, NDSP_THREAD_STACK_SIZE, 0x18, -2, true); ndspThread = threadCreate(ndspThreadMain, 0x0, NDSP_THREAD_STACK_SIZE, 0x18, -2, true);
if (!ndspThread) goto _fail3; if (!ndspThread) goto _fail3;
aptHook(&aptCookie, ndspAptHook, NULL); dspHook(&ndspHookCookie, ndspHookCallback);
return 0; return 0;
_fail3: _fail3:
@ -518,7 +510,7 @@ void ndspExit(void)
LightEvent_Signal(&sleepEvent); LightEvent_Signal(&sleepEvent);
} }
threadJoin(ndspThread, U64_MAX); threadJoin(ndspThread, U64_MAX);
aptUnhook(&aptCookie); dspUnhook(&ndspHookCookie);
if (!bSleeping) if (!bSleeping)
ndspFinalize(false); ndspFinalize(false);
bSleeping = false; bSleeping = false;

View File

@ -55,6 +55,7 @@ enum
FLAG_CANCELLED = BIT(10), FLAG_CANCELLED = BIT(10),
// Miscellaneous // Miscellaneous
FLAG_DSPWAKEUP = BIT(29),
FLAG_CHAINLOAD = BIT(30), FLAG_CHAINLOAD = BIT(30),
FLAG_SPURIOUS = BIT(31), FLAG_SPURIOUS = BIT(31),
}; };
@ -87,6 +88,11 @@ __attribute__((weak)) void _aptDebug(int a, int b) { }
#define _aptDebug(a,b) ((void)0) #define _aptDebug(a,b) ((void)0)
#endif #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) static void aptCallHook(APT_HookType hookType)
{ {
aptHookCookie* c; aptHookCookie* c;
@ -448,16 +454,34 @@ void aptEventHandler(void *arg)
// - APTCMD_DSP_SLEEP (*NOT* cancelled afterwards) // - APTCMD_DSP_SLEEP (*NOT* cancelled afterwards)
// - APTCMD_DSP_WAKEUP (*NOT* cancelled afterwards) // - APTCMD_DSP_WAKEUP (*NOT* cancelled afterwards)
// We will instead only handle spurious APTCMD_WAKEUP_PAUSE parameters // We will handle the following:
switch (cmd)
{
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) // (see aptInit for more details on the hax 2.x spurious wakeup problem)
if (cmd == APTCMD_WAKEUP_PAUSE && (aptFlags & FLAG_SPURIOUS)) if (aptFlags & FLAG_SPURIOUS)
{ {
APT_CancelParameter(APPID_NONE, envGetAptAppId(), NULL); APT_CancelParameter(APPID_NONE, envGetAptAppId(), NULL);
aptFlags &= ~FLAG_SPURIOUS; aptFlags &= ~FLAG_SPURIOUS;
continue; break;
}
// Fallthrough otherwise
default:
// Others not accounted for -> pass it on to aptReceiveParameter
LightEvent_Signal(&aptReceiveEvent);
break;
} }
LightEvent_Signal(&aptReceiveEvent);
continue; continue;
} }
@ -505,6 +529,8 @@ void aptEventHandler(void *arg)
break; break;
case APTSIGNAL_SLEEP_ENTER: case APTSIGNAL_SLEEP_ENTER:
_aptDebug(10, aptFlags); _aptDebug(10, aptFlags);
if (aptDspSleep())
aptFlags |= FLAG_DSPWAKEUP;
if (aptIsActive()) if (aptIsActive())
aptFlags |= FLAG_SHOULDSLEEP; aptFlags |= FLAG_SHOULDSLEEP;
else else
@ -512,6 +538,11 @@ void aptEventHandler(void *arg)
APT_ReplySleepNotificationComplete(envGetAptAppId()); APT_ReplySleepNotificationComplete(envGetAptAppId());
break; break;
case APTSIGNAL_SLEEP_WAKEUP: case APTSIGNAL_SLEEP_WAKEUP:
if (aptFlags & FLAG_DSPWAKEUP)
{
aptFlags &= ~FLAG_DSPWAKEUP;
aptDspWakeup();
}
if (!aptIsActive()) if (!aptIsActive())
break; break;
if (aptFlags & FLAG_SLEEPING) if (aptFlags & FLAG_SLEEPING)
@ -589,8 +620,16 @@ APT_Command aptWaitForWakeUp(APT_Transition transition)
aptCallHook(APTHOOK_ONRESTORE); aptCallHook(APTHOOK_ONRESTORE);
} }
if (cmd == APTCMD_WAKEUP_CANCEL) 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; aptFlags |= FLAG_CANCELLED;
} else if (cmd != APTCMD_WAKEUP_LAUNCHAPP)
{
aptFlags &= ~FLAG_DSPWAKEUP;
aptDspWakeup();
}
if (cmd != APTCMD_WAKEUP_JUMPTOHOME) if (cmd != APTCMD_WAKEUP_JUMPTOHOME)
{ {
@ -732,6 +771,7 @@ void aptJumpToHomeMenu(void)
GSPGPU_SaveVramSysArea(); GSPGPU_SaveVramSysArea();
aptScreenTransfer(aptGetMenuAppID(), true); aptScreenTransfer(aptGetMenuAppID(), true);
aptDspSleep();
GSPGPU_ReleaseRight(); GSPGPU_ReleaseRight();
APT_JumpToHomeMenu(NULL, 0, 0); APT_JumpToHomeMenu(NULL, 0, 0);

View File

@ -9,6 +9,20 @@
static Handle dspHandle; static Handle dspHandle;
static int dspRefCount; 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 dspInit(void)
{ {
Result ret = 0; Result ret = 0;
@ -16,18 +30,91 @@ Result dspInit(void)
if (AtomicPostIncrement(&dspRefCount)) return 0; if (AtomicPostIncrement(&dspRefCount)) return 0;
ret = srvGetServiceHandle(&dspHandle, "dsp::DSP"); ret = srvGetServiceHandle(&dspHandle, "dsp::DSP");
if (R_SUCCEEDED(ret)) DSP_UnloadComponent(); if (R_FAILED(ret))
else AtomicDecrement(&dspRefCount); {
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) void dspExit(void)
{ {
if (AtomicDecrement(&dspRefCount)) return; if (AtomicDecrement(&dspRefCount)) return;
DSP_UnloadComponent();
svcCloseHandle(dspHandle); 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 DSP_GetHeadphoneStatus(bool* is_inserted)
{ {
Result ret = 0; 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) 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; Result ret = 0;
u32* cmdbuf = getThreadCommandBuffer(); u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x11,3,2); 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[4] = IPC_Desc_Buffer(size,IPC_BUFFER_R);
cmdbuf[5] = (u32) component; cmdbuf[5] = (u32) component;
if (R_FAILED(ret = svcSendSyncRequest(dspHandle))) return ret; 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]; return cmdbuf[1];
} }
Result DSP_UnloadComponent(void) Result DSP_UnloadComponent(void)
{ {
if (!dspComponentLoaded)
return 0;
dspComponentLoaded = false;
Result ret = 0; Result ret = 0;
u32* cmdbuf = getThreadCommandBuffer(); u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x12,0,0); cmdbuf[0] = IPC_MakeHeader(0x12,0,0);