From 8583d3beaa517602d87bd49453217cc82fa0bc80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 28 Oct 2026 13:32:47 +0100 Subject: [PATCH] sceNetInet, sceNetApctl: Quicker access point connection, and no busy waits pspautotests net/inet/timing measures, on a PSP that has connected to an open network before: JOINING 11 ms after sceNetApctlConnect, GETTING_IP after about 2.6 s, GOT_IP after about 3.6 s (6.6 s the first time), and DISCONNECTED 1.1 s after a disconnect. We took 2 s to get to each of the three, 6 s in all, and 10 ms to disconnect. Games have to wait for the connection anyway, so it's now quicker than hardware: JOINING after 10 ms, GETTING_IP 1.25 s later and GOT_IP 0.5 s after that, with the delays in named constants. Disconnecting takes 0.5 s, the state staying as it was until then. The handlers also got 0 as the old state of every step but the first. Each step now starts from the state the one before ended in. recv, recvfrom, select and poll still added a 500 us or 1 ms delay to every call, a workaround from before blocking calls waited for real. They now take what they take on the PSP when they return at once, 4-15 us (net/inet/timing). Co-Authored-By: Claude Opus 5.5 --- Core/HLE/sceNet.cpp | 53 +++++++++++++++++++++++++---------------- Core/HLE/sceNetInet.cpp | 29 ++++++++++++++-------- pspautotests | 2 +- 3 files changed, 53 insertions(+), 31 deletions(-) diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 511eaa976f..4acba4ed36 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -697,6 +697,15 @@ void __NetShutdown() { } } +// How long each step of connecting to the access point takes. On a PSP that has connected before +// (an open network, pspautotests net/inet/timing), Connect is in JOINING after 11 ms, GETTING_IP after +// about 2.6 s and GOT_IP after about 3.6 s (6.6 s the first time, getting the address taking longer), +// and Disconnect is back in DISCONNECTED after 1.1 s. Games wait for it anyway, so this is quicker. +constexpr int APCTL_JOINING_DELAY_US = 10000; +constexpr int APCTL_GETTING_IP_DELAY_US = 1250000; +constexpr int APCTL_GOT_IP_DELAY_US = 500000; +constexpr int APCTL_DISCONNECT_DELAY_US = 500000; + static void __UpdateApctlHandlers(u32 oldState, u32 newState, u32 flag, u32 error) { std::lock_guard apctlGuard(apctlEvtMtx); apctlEvents.push_back({ oldState, newState, flag, error }); @@ -823,10 +832,13 @@ void __NetApctlCallbacks() apctlEvents.pop_front(); // Adjust delay according to current event. - if (event == PSP_NET_APCTL_EVENT_CONNECT_REQUEST || event == PSP_NET_APCTL_EVENT_GET_IP || event == PSP_NET_APCTL_EVENT_SCAN_REQUEST || event == PSP_NET_APCTL_EVENT_ESTABLISHED) - delayus = adhocEventDelay; - else - delayus = adhocEventPollDelay; + switch (event) { + case PSP_NET_APCTL_EVENT_CONNECT_REQUEST: delayus = APCTL_JOINING_DELAY_US; break; + case PSP_NET_APCTL_EVENT_ESTABLISHED: delayus = APCTL_GETTING_IP_DELAY_US; break; + case PSP_NET_APCTL_EVENT_GET_IP: delayus = APCTL_GOT_IP_DELAY_US; break; + case PSP_NET_APCTL_EVENT_SCAN_REQUEST: delayus = adhocEventDelay; break; + default: delayus = adhocEventPollDelay; break; + } // Do we need to change the oldState? even if there was error? if (error == 0) @@ -838,22 +850,23 @@ void __NetApctlCallbacks() // Need to make sure netApctlState is updated before calling the callback's mipscall so the game can GetState()/GetInfo() within their handler's subroutine and make use the new State/Info // Should we update NewState & Error accordingly to Event before executing the mipscall ? sceNetApctl* functions might want to set the error value tho, so we probably should leave it untouched, right? //error = 0; + // Each next event starts from the state this one ends in. switch (event) { case PSP_NET_APCTL_EVENT_CONNECT_REQUEST: newState = PSP_NET_APCTL_STATE_JOINING; // Should we set the State to PSP_NET_APCTL_STATE_DISCONNECTED if there was error? if (error != 0) - apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + apctlEvents.push_front({ newState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); else - apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); // Should we use PSP_NET_APCTL_EVENT_EAP_AUTH if securityType is not NONE? + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); // Should we use PSP_NET_APCTL_EVENT_EAP_AUTH if securityType is not NONE? break; case PSP_NET_APCTL_EVENT_ESTABLISHED: newState = PSP_NET_APCTL_STATE_GETTING_IP; // FIXME: Official prx seems to return ERROR 0x80410280 on the next event when using invalid connection profile to Connect? if (error != 0) - apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + apctlEvents.push_front({ newState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); else - apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_GET_IP, 0 }); + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_GET_IP, 0 }); break; case PSP_NET_APCTL_EVENT_GET_IP: @@ -863,21 +876,21 @@ void __NetApctlCallbacks() case PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST: newState = PSP_NET_APCTL_STATE_DISCONNECTED; - delayus = adhocDefaultDelay / 2; // FIXME: Similar to Adhocctl Disconnect, we probably need to change the state within a frame-time (or less to be safer) + delayus = APCTL_DISCONNECT_DELAY_US; break; case PSP_NET_APCTL_EVENT_SCAN_REQUEST: newState = PSP_NET_APCTL_STATE_SCANNING; if (error != 0) - apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + apctlEvents.push_front({ newState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); else - apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_SCAN_COMPLETE, 0 }); + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_SCAN_COMPLETE, 0 }); break; case PSP_NET_APCTL_EVENT_SCAN_COMPLETE: newState = PSP_NET_APCTL_STATE_DISCONNECTED; if (error == 0) - apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_SCAN_STOP, 0 }); + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_SCAN_STOP, 0 }); break; case PSP_NET_APCTL_EVENT_SCAN_STOP: @@ -887,25 +900,25 @@ void __NetApctlCallbacks() case PSP_NET_APCTL_EVENT_EAP_AUTH: // Is this suppose to happen between JOINING and ESTABLISHED ? newState = PSP_NET_APCTL_STATE_EAP_AUTH; if (error != 0) - apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + apctlEvents.push_front({ newState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); else - apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_KEY_EXCHANGE, 0 }); // not sure if KEY_EXCHANGE is the next step after AUTH or not tho + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_KEY_EXCHANGE, 0 }); // not sure if KEY_EXCHANGE is the next step after AUTH or not tho break; case PSP_NET_APCTL_EVENT_KEY_EXCHANGE: // Is this suppose to happen between JOINING and ESTABLISHED ? newState = PSP_NET_APCTL_STATE_KEY_EXCHANGE; if (error != 0) - apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + apctlEvents.push_front({ newState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); else - apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); break; case PSP_NET_APCTL_EVENT_RECONNECT: newState = PSP_NET_APCTL_STATE_DISCONNECTED; if (error != 0) - apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + apctlEvents.push_front({ newState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); else - apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0 }); + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0 }); break; case PSP_NET_APCTL_EVENT_ERROR: @@ -1575,8 +1588,8 @@ int sceNetApctlDisconnect() { // Discards any pending events so we can disconnect immediately apctlEvents.clear(); - __UpdateApctlHandlers(netApctlState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST, 0); - // TODO: Blocks current thread and wait for a state change, but the state should probably need to be changed within 1 frame-time (~16ms) + // The state stays as it is until the event, APCTL_DISCONNECT_DELAY_US later, sets it to DISCONNECTED. + __UpdateApctlHandlers(netApctlState, netApctlState, PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST, 0); return hleLogInfo(Log::sceNet, 0); } diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 19d17d4d7a..e4592edb47 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -922,6 +922,11 @@ static void __NetInetWaitPoll(u64 userdata, int cyclesLate) { } // FIXME: nfds is number of fd(s) as in posix poll, or was it maximum fd value as in posix select? Star Wars Battlefront Renegade seems to set the nfds to 64, while Coded Arms Contagion is using 256 +// What these cost on a PSP when they return without waiting (pspautotests net/inet/timing). +static const int INET_SELECT_US = 15; +static const int INET_POLL_US = 11; +static const int INET_RECV_US = 4; + int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr, u32 timeoutPtr) { INET_CHECK_STACK_SOCKET(); InetThreadSeen(); @@ -957,7 +962,8 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr } attempt.result = InetWouldBlockResult(InetWaitOp::Select, __KernelGetCurThread(), args, attempt.hostErrno, __FUNCTION__); } - return hleDelayResult(hleLogDebug(Log::sceNet, attempt.result), "workaround until blocking-socket", 500); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented (ie. Coded Arms Contagion) + hleEatMicro(INET_SELECT_US); + return hleLogDebug(Log::sceNet, attempt.result); } int sceNetInetPoll(MIPSState *mips, u32 fdsPtr, u32 nfds, int timeout) { @@ -982,7 +988,8 @@ int sceNetInetPoll(MIPSState *mips, u32 fdsPtr, u32 nfds, int timeout) { } attempt.result = 0; } - return hleDelayResult(hleLogDebug(Log::sceNet, attempt.result), "workaround until blocking-socket", 1000); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented + hleEatMicro(INET_POLL_US); + return hleLogDebug(Log::sceNet, attempt.result); } static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) { @@ -1003,12 +1010,14 @@ static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) { return hleLogDebug(Log::sceNet, 0, "waiting"); } attempt.result = InetWouldBlockResult(InetWaitOp::Recv, __KernelGetCurThread(), args, attempt.hostErrno, __FUNCTION__); - return hleDelayResult(hleLogDebug(Log::sceNet, attempt.result, "EAGAIN"), "workaround until blocking-socket", 500); + hleEatMicro(INET_RECV_US); + return hleLogDebug(Log::sceNet, attempt.result, "EAGAIN"); } + hleEatMicro(INET_RECV_US); if (attempt.result < 0) { - return hleDelayResult(hleLogError(Log::sceNet, attempt.result), "workaround until blocking-socket", 500); + return hleLogError(Log::sceNet, attempt.result); } - return hleDelayResult(hleLogInfo(Log::sceNet, attempt.result), "workaround until blocking-socket", 500); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented + return hleLogInfo(Log::sceNet, attempt.result); } static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { @@ -1748,14 +1757,14 @@ static int sceNetInetRecvfrom(int socket, u32 bufferPtr, int len, int flags, u32 return hleLogDebug(Log::sceNet, 0, "waiting"); } attempt.result = InetWouldBlockResult(InetWaitOp::RecvFrom, __KernelGetCurThread(), args, attempt.hostErrno, __FUNCTION__); - // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented (ie. Coded Arms Contagion) - return hleDelayResult(hleLogDebug(Log::sceNet, attempt.result, "EAGAIN"), "workaround until blocking-socket", 500); + hleEatMicro(INET_RECV_US); + return hleLogDebug(Log::sceNet, attempt.result, "EAGAIN"); } + hleEatMicro(INET_RECV_US); if (attempt.result < 0) { - return hleDelayResult(hleLogError(Log::sceNet, attempt.result), "workaround until blocking-socket", 500); + return hleLogError(Log::sceNet, attempt.result); } - // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented (ie. Coded Arms Contagion) - return hleDelayResult(hleLogDebug(Log::sceNet, attempt.result), "workaround until blocking-socket", 500); + return hleLogDebug(Log::sceNet, attempt.result); } // args: socket, buf, len, flags, to, tolen. diff --git a/pspautotests b/pspautotests index 6ce14e13ee..2321faa063 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 6ce14e13ee1f1b7be876e38c467d9f52b0d7c6e0 +Subproject commit 2321faa063280a6370b450bfc5f3859fc590360f