Miscellaneous APT fixes and cleanup, see details:

- APT parameter handling now follows official sw a bit more closely
  (with the APT thread acting as a filter which then relays the
   status to the main thread using a LightEvent)
- RUNFLAG_APTREINIT (hax 2.x) handling code has been simplified and
  made (hopefully) more robust
- Fixed sleep handling when the app is inactive (i.e. app is suspended)
- APT debug callback now guarded by an #ifdef (LIBCTRU_APT_DEBUG)
- Removed old Citra response cmdheader workaround
- Other miscellaneous fixes I probably forgot to talk about
This commit is contained in:
fincs 2020-06-10 02:08:01 +02:00
parent 59b8fd8283
commit 7171a87d53
No known key found for this signature in database
GPG Key ID: 62C7609ADA219C60

View File

@ -22,6 +22,7 @@
static int aptRefCount = 0;
static Handle aptLockHandle;
static Handle aptEvents[2];
static LightEvent aptReceiveEvent;
static LightEvent aptSleepEvent;
static Thread aptEventHandlerThread;
static bool aptEventHandlerThreadQuit;
@ -39,12 +40,13 @@ enum
FLAG_WKUPBYCANCEL = BIT(5),
FLAG_WANTSTOSLEEP = BIT(6),
FLAG_SLEEPING = BIT(7),
FLAG_SPURIOUS = BIT(30),
FLAG_EXITED = BIT(31),
};
static u8 aptHomeButtonState;
static u8 aptRecentHomeButtonState;
static u32 aptFlags = FLAG_ALLOWSLEEP;
static u32 aptFlags;
static bool aptHomeAllowed = true;
static u32 aptParameters[0x1000/4];
static u64 aptChainloadTid;
@ -64,10 +66,12 @@ typedef enum
static void aptEventHandler(void *arg);
static APT_Command aptWaitForWakeUp(APT_Transition transition);
// The following function can be overriden in order to log APT signals and notifications for debugging purposes
__attribute__((weak)) void _aptDebug(int a, int b)
{
}
// The following function can be overridden in order to log APT signals and notifications for debugging purposes
#ifdef LIBCTRU_APT_DEBUG
__attribute__((weak)) void _aptDebug(int a, int b) { }
#else
#define _aptDebug(a,b) ((void)0)
#endif
static void aptCallHook(APT_HookType hookType)
{
@ -133,7 +137,7 @@ Result aptSendCommand(u32* aptcmdbuf)
res = svcSendSyncRequest(aptuHandle);
if (R_SUCCEEDED(res))
{
memcpy(aptcmdbuf, cmdbuf, 4*16);//4*countPrmWords(cmdbuf[0])); // Workaround for Citra failing to emulate response cmdheaders
memcpy(aptcmdbuf, cmdbuf, 4*countPrmWords(cmdbuf[0]));
res = aptcmdbuf[1];
}
svcCloseHandle(aptuHandle);
@ -142,20 +146,6 @@ Result aptSendCommand(u32* aptcmdbuf)
return res;
}
static void aptClearParamQueue(void)
{
// Check for parameters?
for (;;)
{
APT_Command cmd;
Result res = APT_GlanceParameter(envGetAptAppId(), aptParameters, sizeof(aptParameters), NULL, &cmd, NULL, NULL);
if (R_FAILED(res) || cmd==APTCMD_NONE) break;
_aptDebug(2, cmd);
svcClearEvent(aptEvents[1]);
APT_CancelParameter(APPID_NONE, envGetAptAppId(), NULL);
}
}
static void aptInitCaptureInfo(aptCaptureBufInfo* capinfo)
{
GSPGPU_CaptureInfo gspcapinfo;
@ -203,11 +193,8 @@ Result aptInit(void)
ret = APT_Initialize(envGetAptAppId(), attr, &aptEvents[0], &aptEvents[1]);
if (R_FAILED(ret)) goto _fail2;
// Enable APT
ret = APT_Enable(attr);
if (R_FAILED(ret)) goto _fail3;
// Initialize APT sleep event
// Initialize light events
LightEvent_Init(&aptReceiveEvent, RESET_STICKY);
LightEvent_Init(&aptSleepEvent, RESET_ONESHOT);
// Create APT event handler thread
@ -215,28 +202,23 @@ Result aptInit(void)
aptEventHandlerThread = threadCreate(aptEventHandler, 0x0, APT_HANDLER_STACKSIZE, 0x31, -2, true);
if (!aptEventHandlerThread) goto _fail3;
// Enable APT
aptFlags = FLAG_ALLOWSLEEP;
ret = APT_Enable(attr);
if (R_FAILED(ret)) goto _fail3;
// Get information about ourselves
APT_GetAppletInfo(envGetAptAppId(), &aptChainloadTid, &aptChainloadMediatype, NULL, NULL, NULL);
// Special handling for aptReinit (aka hax)
APT_Transition transition = TR_ENABLE;
if (aptIsReinit())
{
transition = TR_JUMPTOMENU;
// Clear out any pending parameters
bool success = false;
do
ret = APT_CancelParameter(APPID_NONE, envGetAptAppId(), &success);
while (success);
// APT thinks the application is suspended, so we need to tell it to unsuspend us.
APT_PrepareToJumpToApplication(false);
APT_JumpToApplication(NULL, 0, 0);
}
// Wait for wakeup
aptWaitForWakeUp(transition);
aptWaitForWakeUp(TR_ENABLE);
// Special handling for aptReinit (aka hax 2.x):
// In certain cases when running under hax 2.x, we may receive a spurious
// second wakeup command. Therefore we must silently drop it in order to
// avoid keeping stale commands in APT's internal buffer.
if (aptIsReinit())
aptFlags |= FLAG_SPURIOUS;
return 0;
_fail3:
@ -336,10 +318,7 @@ void aptExit(void)
closeAptLock = false;
srvInit(); // Keep srv initialized
} else
{
APT_Finalize(envGetAptAppId());
aptClearParamQueue();
}
aptEventHandlerThreadQuit = true;
svcSignalEvent(aptEvents[0]);
@ -358,22 +337,59 @@ void aptEventHandler(void *arg)
while (!aptEventHandlerThreadQuit)
{
s32 id = 0;
svcWaitSynchronizationN(&id, aptEvents, 1, 0, U64_MAX);
svcWaitSynchronizationN(&id, aptEvents, 2, 0, U64_MAX);
if (aptEventHandlerThreadQuit)
break;
// If the receive event is still signaled, sleep for a bit and retry
if (LightEvent_TryWait(&aptReceiveEvent))
{
_aptDebug(222, 0);
svcSleepThread(10000000); // 10ms
svcSignalEvent(aptEvents[id]);
continue;
}
// This is done by official sw, even though APT events are oneshot...
svcClearEvent(aptEvents[id]);
if (id != 0)
// Relay receive events to our light event
if (id == 1)
{
NS_APPID sender;
APT_Command cmd;
Result res = APT_GlanceParameter(envGetAptAppId(), aptParameters, sizeof(aptParameters), &sender, &cmd, NULL, NULL);
if (R_FAILED(res))
continue; // Official sw panics here - we instead swallow the (non-)event.
_aptDebug(2, cmd); _aptDebug(22, sender);
// NOTE: Official software handles the following parameter types here:
// - APTCMD_MESSAGE (cancelled afterwards) (we handle it in aptReceiveParameter instead)
// - APTCMD_REQUEST (cancelled afterwards) (only sent to and handled by libapplets?)
// - 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))
{
APT_CancelParameter(APPID_NONE, envGetAptAppId(), NULL);
aptFlags &= ~FLAG_SPURIOUS;
continue;
}
LightEvent_Signal(&aptReceiveEvent);
continue;
}
APT_Signal signal;
Result res = APT_InquireNotification(envGetAptAppId(), &signal);
if (R_FAILED(res))
continue;
_aptDebug(1, signal);
switch (signal)
{
case APTSIGNAL_HOMEBUTTON:
@ -383,15 +399,38 @@ void aptEventHandler(void *arg)
if (!aptHomeButtonState) aptHomeButtonState = 2;
break;
case APTSIGNAL_SLEEP_QUERY:
APT_ReplySleepQuery(envGetAptAppId(), aptIsSleepAllowed() ? APTREPLY_ACCEPT : APTREPLY_REJECT);
{
APT_QueryReply reply;
if (aptFlags & (FLAG_ORDERTOCLOSE|FLAG_WKUPBYCANCEL))
// Reject sleep if we are expected to close
reply = APTREPLY_REJECT;
else if (aptFlags & FLAG_ACTIVE)
// Accept sleep based on user setting if we are active
reply = aptIsSleepAllowed() ? APTREPLY_ACCEPT : APTREPLY_REJECT;
else
// Accept sleep if we are inactive regardless of user setting
reply = APTREPLY_ACCEPT;
_aptDebug(10, aptFlags);
_aptDebug(11, reply);
APT_ReplySleepQuery(envGetAptAppId(), reply);
break;
}
case APTSIGNAL_SLEEP_CANCEL:
// Do something maybe?
if (aptFlags & FLAG_ACTIVE)
aptFlags &= ~FLAG_WANTSTOSLEEP;
break;
case APTSIGNAL_SLEEP_ENTER:
aptFlags |= FLAG_WANTSTOSLEEP;
_aptDebug(10, aptFlags);
if (aptFlags & FLAG_ACTIVE)
aptFlags |= FLAG_WANTSTOSLEEP;
else
// Since we are not active, this must be handled here.
APT_ReplySleepNotificationComplete(envGetAptAppId());
break;
case APTSIGNAL_SLEEP_WAKEUP:
if (!(aptFlags & FLAG_ACTIVE))
break;
if (aptFlags & FLAG_SLEEPING)
LightEvent_Signal(&aptSleepEvent);
else
@ -407,7 +446,16 @@ void aptEventHandler(void *arg)
aptFlags &= ~FLAG_POWERBUTTON;
break;
case APTSIGNAL_TRY_SLEEP:
{
// Official software performs this APT_SleepSystem command here, although
// its purpose is unclear. For completeness' sake, we'll do it as well.
static const struct PtmWakeEvents s_sleepWakeEvents = {
.pdn_wake_events = 0,
.mcu_interupt_mask = BIT(6),
};
APT_SleepSystem(&s_sleepWakeEvents);
break;
}
case APTSIGNAL_ORDERTOCLOSE:
aptFlags |= FLAG_ORDERTOCLOSE;
break;
@ -423,8 +471,8 @@ static Result aptReceiveParameter(APT_Command* cmd, size_t* actualSize, Handle*
size_t temp_actualSize;
if (!actualSize) actualSize = &temp_actualSize;
svcWaitSynchronization(aptEvents[1], U64_MAX);
svcClearEvent(aptEvents[1]);
LightEvent_Wait(&aptReceiveEvent);
LightEvent_Clear(&aptReceiveEvent);
Result res = APT_ReceiveParameter(envGetAptAppId(), aptParameters, sizeof(aptParameters), &sender, cmd, actualSize, handle);
if (R_SUCCEEDED(res) && *cmd == APTCMD_MESSAGE && aptMessageFunc)
aptMessageFunc(aptMessageFuncData, sender, aptParameters, *actualSize);
@ -524,7 +572,7 @@ static void aptProcessJumpToMenu(void)
bool sleep = aptIsSleepAllowed();
aptSetSleepAllowed(false);
aptClearParamQueue();
aptFlags &= ~FLAG_SPURIOUS; // If we haven't received a spurious wakeup by now, we probably never will (see aptInit)
APT_PrepareToJumpToHomeMenu();
aptScreenTransfer(aptGetMenuAppID(), true);
@ -563,6 +611,7 @@ bool aptMainLoop(void)
aptRecentHomeButtonState = aptHomeButtonState;
aptHomeButtonState = 0;
APT_UnlockTransition(0x01);
APT_SleepIfShellClosed();
}
}
@ -611,7 +660,7 @@ bool aptLaunchLibraryApplet(NS_APPID appId, void* buf, size_t bufsize, Handle ha
bool sleep = aptIsSleepAllowed();
aptSetSleepAllowed(false);
aptClearParamQueue();
aptFlags &= ~FLAG_SPURIOUS; // If we haven't received a spurious wakeup by now, we probably never will (see aptInit)
APT_PrepareToStartLibraryApplet(appId);
aptSetSleepAllowed(sleep);
@ -769,10 +818,7 @@ Result APT_InquireNotification(u32 appID, APT_Signal* signalType)
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
{
_aptDebug(1, cmdbuf[2]);
*signalType=cmdbuf[2];
}
return ret;
}
@ -929,7 +975,6 @@ Result APT_ReceiveParameter(NS_APPID appID, void* buffer, size_t bufferSize, NS_
if (R_SUCCEEDED(ret))
{
_aptDebug(2, cmdbuf[3]);
if (sender) *sender =cmdbuf[2];
if (command) *command =cmdbuf[3];
if (actualSize) *actualSize=cmdbuf[4];