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

View File

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

View File

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

View File

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