From 47a5d87c4ab14c5ae84eb41975a23208e6535ff6 Mon Sep 17 00:00:00 2001 From: Francisco Javier Trujillo Mata Date: Thu, 29 Oct 2026 10:07:50 +0100 Subject: [PATCH] Handle PSP asynchronous USB receive lifecycle and safe retries Distinguish successful empty completions, cancelled receive requests and retryable errors. Handle detach before async completion, verify USB state before rearming, and retry transient failures through timed event waits without blocking attach/detach processing. Validate receive bounds, mark connected only after successful initial submission and fix event flag cleanup during module shutdown. --- usbhostfs/main.c | 99 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 15 deletions(-) diff --git a/usbhostfs/main.c b/usbhostfs/main.c index 36a227f..553dc13 100644 --- a/usbhostfs/main.c +++ b/usbhostfs/main.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -1017,39 +1018,103 @@ int usb_thread(SceSize size, void *argp) { int ret; u32 result; + int retry_async = 0; DEBUG_PRINTF("USB Thread Started\n"); while(1) { + SceUInt timeout = 100000; ret = sceKernelWaitEventFlag(g_mainevent, USB_EVENT_ATTACH | USB_EVENT_DETACH | USB_EVENT_ASYNC - , PSP_EVENT_WAITOR | PSP_EVENT_WAITCLEAR, &result, NULL); + , PSP_EVENT_WAITOR | PSP_EVENT_WAITCLEAR, &result, + retry_async ? &timeout : NULL); + if(retry_async && (ret == (int)SCE_KERNEL_ERROR_WAIT_TIMEOUT)) + { + /* Retry while processing attach/detach events without delay. */ + if(g_connected && (sceUsbGetState() & PSP_USB_CONNECTION_ESTABLISHED)) + { + if(set_ayncreq(async_data, sizeof(async_data)) >= 0) + retry_async = 0; + } + else + { + retry_async = 0; + g_connected = 0; + sceKernelClearEventFlag(g_mainevent, ~USB_EVENT_CONNECT); + } + continue; + } if(ret < 0) { DEBUG_PRINTF("Error waiting on event flag %08X\n", ret); sceKernelExitDeleteThread(0); } - if(result & USB_EVENT_ASYNC) - { - DEBUG_PRINTF("Async Request Done %d %d\n", g_async_req.retcode, g_async_req.recvsize); - if((g_async_req.retcode == 0) && (g_async_req.recvsize > 0)) - { - fill_async(async_data, g_async_req.recvsize); - set_ayncreq(async_data, sizeof(async_data)); - } - } - if(result & USB_EVENT_DETACH) { g_connected = 0; + retry_async = 0; sceKernelClearEventFlag(g_mainevent, ~USB_EVENT_CONNECT); DEBUG_PRINTF("USB Detach occurred\n"); } + if(result & USB_EVENT_ASYNC) + { + DEBUG_PRINTF("Async Request Done %d %d\n", g_async_req.retcode, g_async_req.recvsize); + if(g_connected) + { + if(g_async_req.retcode == -3) + { + /* -3 means cancelled, not a transient receive failure. + * Wait for the normal USB attach/HELLO sequence. */ + DEBUG_PRINTF("Async USB receive cancelled; awaiting reconnect\n"); + g_connected = 0; + retry_async = 0; + sceKernelClearEventFlag(g_mainevent, ~USB_EVENT_CONNECT); + } + else + { + if(g_async_req.retcode == 0) + { + if((g_async_req.recvsize > 0) && + (g_async_req.recvsize <= sizeof(async_data))) + fill_async(async_data, g_async_req.recvsize); + else if((g_async_req.recvsize < 0) || + (g_async_req.recvsize > sizeof(async_data))) + MODPRINTF("Invalid async USB receive size %d\n", + g_async_req.recvsize); + /* A successful zero-length packet needs no delay. */ + } + else + { + MODPRINTF("Async USB receive error %08X\n", g_async_req.retcode); + /* Requeue after a bounded event wait; do not block detach. */ + retry_async = 1; + } + + /* Do not queue new work on a disconnected USB bus. */ + if(g_connected && (sceUsbGetState() & PSP_USB_CONNECTION_ESTABLISHED)) + { + if(!retry_async && (set_ayncreq(async_data, sizeof(async_data)) < 0)) + { + MODPRINTF("Could not restart async USB receive; retrying\n"); + retry_async = 1; + } + } + else + { + retry_async = 0; + g_connected = 0; + sceKernelClearEventFlag(g_mainevent, ~USB_EVENT_CONNECT); + } + } + } + } + if(result & USB_EVENT_ATTACH) { uint32_t magic; g_connected = 0; + retry_async = 0; sceKernelClearEventFlag(g_mainevent, ~USB_EVENT_CONNECT); DEBUG_PRINTF("USB Attach occurred\n"); if(read_data(&magic, sizeof(magic)) == sizeof(magic)) @@ -1058,9 +1123,11 @@ int usb_thread(SceSize size, void *argp) { if(send_hello_cmd()) { - set_ayncreq(async_data, sizeof(async_data)); - g_connected = 1; - sceKernelSetEventFlag(g_mainevent, USB_EVENT_CONNECT); + if(set_ayncreq(async_data, sizeof(async_data)) >= 0) + { + g_connected = 1; + sceKernelSetEventFlag(g_mainevent, USB_EVENT_CONNECT); + } } } } @@ -1076,6 +1143,7 @@ int start_func(int size, void *p) int ret; DEBUG_PRINTF("Start Function %p\n", p); + g_connected = 0; /* Fill in the descriptor tables */ memset(usbdata, 0, sizeof(usbdata)); @@ -1171,6 +1239,7 @@ int start_func(int size, void *p) int stop_func(int size, void *p) { DEBUG_PRINTF("Stop function %p\n", p); + g_connected = 0; if(g_thid >= 0) { @@ -1188,7 +1257,7 @@ int stop_func(int size, void *p) if(g_transevent >= 0) { sceKernelDeleteEventFlag(g_transevent); - g_mainevent = -1; + g_transevent = -1; } if(g_asyncevent >= 0)