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 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-28 20:30:07 +01:00
1 parent 7aecf86c4d
commit 8583d3beaa
3 files changed
+53 -31

No files matched your search

+33 -20
View File
@@ -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<std::recursive_mutex> 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);
}
+19 -10
View File
@@ -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.