From cfbeadccc754046084dbbfcb256761afe45ce4b3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 15 Feb 2021 13:43:05 -0800 Subject: [PATCH] Usb: Implement state waits. See #11067 - implementing in hopes of reducing spin. --- Core/HLE/sceKernelThread.cpp | 20 ++--- Core/HLE/sceKernelThread.h | 1 + Core/HLE/sceUsb.cpp | 143 ++++++++++++++++++++++++++++++----- 3 files changed, 131 insertions(+), 33 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 92c5fee535..c784d8e8d5 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -50,11 +50,10 @@ #include "Core/HLE/KernelWaitHelpers.h" #include "Core/HLE/ThreadQueueList.h" -typedef struct -{ +struct WaitTypeNames { WaitType type; const char *name; -} WaitTypeNames; +}; const WaitTypeNames waitTypeNames[] = { { WAITTYPE_NONE, "None" }, @@ -83,18 +82,13 @@ const WaitTypeNames waitTypeNames[] = { { WAITTYPE_ASYNCIO, "AsyncIO" }, { WAITTYPE_MICINPUT, "Microphone input"}, { WAITTYPE_NET, "Network"}, + { WAITTYPE_USB, "USB" }, }; -const char *getWaitTypeName(WaitType type) -{ - int waitTypeNamesAmount = sizeof(waitTypeNames)/sizeof(WaitTypeNames); - - for (int i = 0; i < waitTypeNamesAmount; i++) - { - if (waitTypeNames[i].type == type) - { - return waitTypeNames[i].name; - } +const char *getWaitTypeName(WaitType type) { + for (WaitTypeNames info : waitTypeNames) { + if (info.type == type) + return info.name; } return "Unknown"; diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 3df1d70fc0..7c3f122c2b 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -110,6 +110,7 @@ enum WaitType : int WAITTYPE_ASYNCIO = 23, WAITTYPE_MICINPUT = 24, // fake WAITTYPE_NET = 25, // fake + WAITTYPE_USB = 26, // fake NUM_WAITTYPES }; diff --git a/Core/HLE/sceUsb.cpp b/Core/HLE/sceUsb.cpp index 520b057826..14c017f8fa 100644 --- a/Core/HLE/sceUsb.cpp +++ b/Core/HLE/sceUsb.cpp @@ -17,18 +17,26 @@ #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" +#include "Core/CoreTiming.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" -#include "Core/MIPS/MIPS.h" -#include "Core/CoreTiming.h" +#include "Core/HLE/KernelWaitHelpers.h" +#include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceUsb.h" +#include "Core/MIPS/MIPS.h" +#include "Core/Reporting.h" + +static constexpr uint32_t ERROR_USB_WAIT_TIMEOUT = 0x80243008; // TODO: Map by driver name -bool usbStarted = false; +static bool usbStarted = false; // TODO: Check actual status -bool usbConnected = true; +static bool usbConnected = true; // TODO: Activation by product id -bool usbActivated = false; +static bool usbActivated = false; + +static int usbWaitTimer = -1; +static std::vector waitingThreads; enum UsbStatus { USB_STATUS_STOPPED = 0x001, @@ -39,16 +47,85 @@ enum UsbStatus { USB_STATUS_ACTIVATED = 0x200, }; -void __UsbInit() -{ +static int UsbCurrentState() { + int state = 0; + if (usbStarted) { + state = USB_STATUS_STARTED + | (usbConnected ? USB_STATUS_CONNECTED : USB_STATUS_DISCONNECTED) + | (usbActivated ? USB_STATUS_ACTIVATED : USB_STATUS_DEACTIVATED); + } + return state; +} + +static bool UsbMatchState(int state, u32 mode) { + int match = state & UsbCurrentState(); + if (mode == 0) { + return match == state; + } + return match != 0; +} + +static void UsbSetTimeout(PSPPointer timeout) { + if (!timeout.IsValid() || usbWaitTimer == -1) + return; + + // This should call __UsbWaitTimeout() later, unless we cancel it. + CoreTiming::ScheduleEvent(usToCycles(*timeout), usbWaitTimer, __KernelGetCurThread()); +} + +static void UsbWaitExecTimeout(u64 userdata, int cycleslate) { + u32 error; + SceUID threadID = (SceUID)userdata; + + PSPPointer timeout = PSPPointer::Create(__KernelGetWaitTimeoutPtr(threadID, error)); + if (timeout.IsValid()) + *timeout = 0; + + HLEKernel::RemoveWaitingThread(waitingThreads, threadID); + __KernelResumeThreadFromWait(threadID, ERROR_USB_WAIT_TIMEOUT); + __KernelReSchedule("wait timed out"); +} + +static void UsbUpdateState() { + u32 error; + bool wokeThreads = false; + for (size_t i = 0; i < waitingThreads.size(); ++i) { + SceUID threadID = waitingThreads[i]; + int state = __KernelGetWaitID(threadID, WAITTYPE_USB, error); + if (error != 0) + continue; + + u32 mode = __KernelGetWaitValue(threadID, error); + if (UsbMatchState(state, mode)) { + waitingThreads.erase(waitingThreads.begin() + i); + --i; + + PSPPointer timeout = PSPPointer::Create(__KernelGetWaitTimeoutPtr(threadID, error)); + if (timeout.IsValid() && usbWaitTimer != -1) { + // Remove any event for this thread. + s64 cyclesLeft = CoreTiming::UnscheduleEvent(usbWaitTimer, threadID); + *timeout = (int)cyclesToUs(cyclesLeft); + } + + __KernelResumeThreadFromWait(threadID, UsbCurrentState()); + } + } + + if (wokeThreads) + hleReSchedule("usb state change"); +} + +void __UsbInit() { usbStarted = false; usbConnected = true; usbActivated = false; + waitingThreads.clear(); + + usbWaitTimer = CoreTiming::RegisterEvent("UsbWaitTimeout", UsbWaitExecTimeout); } -void __UsbDoState(PointerWrap &p) -{ - auto s = p.Section("sceUsb", 1, 2); +void __UsbDoState(PointerWrap &p) { + auto s = p.Section("sceUsb", 1, 3); if (!s) return; @@ -60,17 +137,27 @@ void __UsbDoState(PointerWrap &p) usbConnected = true; } Do(p, usbActivated); + if (s >= 3) { + Do(p, waitingThreads); + Do(p, usbWaitTimer); + } else { + waitingThreads.clear(); + usbWaitTimer = -1; + } + CoreTiming::RestoreRegisterEvent(usbWaitTimer, "UsbWaitTimeout", UsbWaitExecTimeout); } static int sceUsbStart(const char* driverName, u32 argsSize, u32 argsPtr) { INFO_LOG(HLE, "sceUsbStart(%s, %i, %08x)", driverName, argsSize, argsPtr); usbStarted = true; + UsbUpdateState(); return 0; } static int sceUsbStop(const char* driverName, u32 argsSize, u32 argsPtr) { INFO_LOG(HLE, "sceUsbStop(%s, %i, %08x)", driverName, argsSize, argsPtr); usbStarted = false; + UsbUpdateState(); return 0; } @@ -79,9 +166,7 @@ static int sceUsbGetState() { if (!usbStarted) { state = 0x80243007; } else { - state = USB_STATUS_STARTED - | (usbConnected ? USB_STATUS_CONNECTED : USB_STATUS_DISCONNECTED) - | (usbActivated ? USB_STATUS_ACTIVATED : USB_STATUS_DEACTIVATED); + state = UsbCurrentState(); } DEBUG_LOG(HLE, "sceUsbGetState: 0x%x", state); return state; @@ -90,22 +175,40 @@ static int sceUsbGetState() { static int sceUsbActivate(u32 pid) { INFO_LOG(HLE, "sceUsbActivate(%i)", pid); usbActivated = true; + UsbUpdateState(); return 0; } static int sceUsbDeactivate(u32 pid) { INFO_LOG(HLE, "sceUsbDeactivate(%i)", pid); usbActivated = false; + UsbUpdateState(); return 0; } -static int sceUsbWaitState(int state, int waitMode, u32 timeoutAddr) { - ERROR_LOG(HLE, "UNIMPL sceUsbWaitStat(%i, %i, %08x)", state, waitMode, timeoutAddr); - return sceUsbGetState(); +static int sceUsbWaitState(int state, u32 waitMode, u32 timeoutPtr) { + hleEatCycles(10000); + + if (waitMode >= 2) + return hleLogError(HLE, SCE_KERNEL_ERROR_ILLEGAL_MODE, "invalid mode"); + if (state == 0) + return hleLogError(HLE, SCE_KERNEL_ERROR_EVF_ILPAT, "bad state"); + + if (UsbMatchState(state, waitMode)) { + return hleLogSuccessX(HLE, UsbCurrentState()); + } + + // We'll have to wait as long as it takes. Cleanup first, just in case. + HLEKernel::RemoveWaitingThread(waitingThreads, __KernelGetCurThread()); + waitingThreads.push_back(__KernelGetCurThread()); + + UsbSetTimeout(PSPPointer::Create(timeoutPtr)); + __KernelWaitCurThread(WAITTYPE_USB, state, waitMode, timeoutPtr, false, "usb state waited"); + return hleLogSuccessI(HLE, 0, "waiting"); } -static int sceUsbWaitStateCB(int state, int waitMode, u32 timeoutAddr) { - ERROR_LOG(HLE, "UNIMPL sceUsbWaitStateCB(%i, %i, %08x)", state, waitMode, timeoutAddr); +static int sceUsbWaitStateCB(int state, u32 waitMode, u32 timeoutPtr) { + ERROR_LOG_REPORT(HLE, "UNIMPL sceUsbWaitStateCB(%i, %i, %08x)", state, waitMode, timeoutPtr); return 0; } @@ -118,8 +221,8 @@ const HLEFunction sceUsb[] = {0X112CC951, nullptr, "sceUsbGetDrvState", '?', "" }, {0X586DB82C, &WrapI_U, "sceUsbActivate", 'i', "x" }, {0XC572A9C8, &WrapI_U, "sceUsbDeactivate", 'i', "x" }, - {0X5BE0E002, &WrapI_IIU, "sceUsbWaitState", '?', "xxx"}, - {0X616F2B61, &WrapI_IIU, "sceUsbWaitStateCB", '?', "xxx"}, + {0X5BE0E002, &WrapI_IUU, "sceUsbWaitState", 'x', "xip"}, + {0X616F2B61, &WrapI_IUU, "sceUsbWaitStateCB", 'x', "xip"}, {0X1C360735, nullptr, "sceUsbWaitCancel", '?', "" }, };