Skip to content

Commit

Permalink
Merge pull request #13970 from unknownbrackets/umd-wait-stat
Browse files Browse the repository at this point in the history
Correct UMD stat waiting when changing discs
  • Loading branch information
hrydgard committed Apr 11, 2021
2 parents 6f332dd + cfb52bf commit 9783a2e
Showing 1 changed file with 70 additions and 80 deletions.
150 changes: 70 additions & 80 deletions Core/HLE/sceUmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@
#include "Core/FileSystems/ISOFileSystem.h"
#include "Core/FileSystems/VirtualDiscFileSystem.h"

const u64 MICRO_DELAY_ACTIVATE = 4000;
static constexpr u64 MICRO_DELAY_ACTIVATE = 4000;
// Does not include PSP_UMD_CHANGED.
static constexpr uint32_t UMD_STAT_ALLOW_WAIT = PSP_UMD_NOT_PRESENT | PSP_UMD_PRESENT | PSP_UMD_NOT_READY | PSP_UMD_READY | PSP_UMD_READABLE;

static u8 umdActivated = 1;
static bool umdActivated = true;
static u32 umdStatus = 0;
static u32 umdErrorStat = 0;
static int driveCBId = 0;
Expand All @@ -60,18 +62,18 @@ struct PspUmdInfo {
u32_le type;
};

void __UmdStatTimeout(u64 userdata, int cyclesLate);
void __UmdStatChange(u64 userdata, int cyclesLate);
void __UmdInsertChange(u64 userdata, int cyclesLate);
void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId);
void __UmdEndCallback(SceUID threadID, SceUID prevCallbackId);
static void __UmdStatTimeout(u64 userdata, int cyclesLate);
static void __UmdStatChange(u64 userdata, int cyclesLate);
static void __UmdInsertChange(u64 userdata, int cyclesLate);
static void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId);
static void __UmdEndCallback(SceUID threadID, SceUID prevCallbackId);

void __UmdInit()
{
umdStatTimeoutEvent = CoreTiming::RegisterEvent("UmdTimeout", __UmdStatTimeout);
umdStatChangeEvent = CoreTiming::RegisterEvent("UmdChange", __UmdStatChange);
umdInsertChangeEvent = CoreTiming::RegisterEvent("UmdInsertChange", __UmdInsertChange);
umdActivated = 1;
umdActivated = true;
umdStatus = 0;
umdErrorStat = 0;
driveCBId = 0;
Expand All @@ -87,7 +89,9 @@ void __UmdDoState(PointerWrap &p)
if (!s)
return;

u8 activatedByte = umdActivated ? 1 : 0;
Do(p, umdActivated);
umdActivated = activatedByte != 0;
Do(p, umdStatus);
Do(p, umdErrorStat);
Do(p, driveCBId);
Expand All @@ -113,8 +117,11 @@ void __UmdDoState(PointerWrap &p)
CoreTiming::RestoreRegisterEvent(umdInsertChangeEvent, "UmdInsertChange", __UmdInsertChange);
}

static u8 __KernelUmdGetState()
{
static u8 __KernelUmdGetState() {
if (!UMDInserted) {
return PSP_UMD_NOT_PRESENT;
}

// Most games seem to expect the disc to be ready early on, active or not.
// It seems like the PSP sets this state when the disc is "ready".
u8 state = PSP_UMD_PRESENT | PSP_UMD_READY;
Expand All @@ -124,36 +131,40 @@ static u8 __KernelUmdGetState()
return state;
}

void __UmdInsertChange(u64 userdata, int cyclesLate)
{
UMDInserted = true;
}

void __UmdStatChange(u64 userdata, int cyclesLate)
{
// TODO: Why not a bool anyway?
umdActivated = userdata & 0xFF;

static void UmdWakeThreads() {
// Wake anyone waiting on this.
for (size_t i = 0; i < umdWaitingThreads.size(); ++i) {
const SceUID threadID = umdWaitingThreads[i];

u32 error;
u32 stat = __KernelGetWaitValue(threadID, error);
bool keep = false;
if (HLEKernel::VerifyWait(threadID, WAITTYPE_UMD, 1)) {
if ((stat & __KernelUmdGetState()) != 0)
__KernelResumeThreadFromWait(threadID, 0);
if (!HLEKernel::VerifyWait(threadID, WAITTYPE_UMD, 1)) {
// Only if they are still waiting do we keep them in the list.
else
keep = true;
keep = (stat & __KernelUmdGetState()) == 0;
if (!keep) {
__KernelResumeThreadFromWait(threadID, 0);
}
}

if (!keep)
if (!keep) {
umdWaitingThreads.erase(umdWaitingThreads.begin() + i--);
}
}
}

static void __UmdStatChange(u64 userdata, int cyclesLate) {
umdActivated = userdata != 0;

UmdWakeThreads();
}

static void __UmdInsertChange(u64 userdata, int cyclesLate) {
UMDInserted = true;

UmdWakeThreads();
}

static void __KernelUmdActivate()
{
u32 notifyArg = PSP_UMD_PRESENT | PSP_UMD_READABLE;
Expand All @@ -179,7 +190,7 @@ static void __KernelUmdDeactivate()
__UmdStatChange(0, 0);
}

void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId)
static void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId)
{
SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;

Expand All @@ -205,7 +216,7 @@ void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId)
WARN_LOG_REPORT(SCEIO, "sceUmdWaitDriveStatCB: beginning callback with bad wait id?");
}

void __UmdEndCallback(SceUID threadID, SceUID prevCallbackId)
static void __UmdEndCallback(SceUID threadID, SceUID prevCallbackId)
{
SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;

Expand Down Expand Up @@ -269,20 +280,16 @@ static u32 sceUmdGetDiscInfo(u32 infoAddr)
return PSP_ERROR_UMD_INVALID_PARAM;
}

static int sceUmdActivate(u32 mode, const char *name)
{
static int sceUmdActivate(u32 mode, const char *name) {
if (mode < 1 || mode > 2)
return PSP_ERROR_UMD_INVALID_PARAM;
return hleLogWarning(SCEIO, PSP_ERROR_UMD_INVALID_PARAM);

__KernelUmdActivate();

if (mode == 1) {
DEBUG_LOG(SCEIO, "0=sceUmdActivate(%d, %s)", mode, name);
} else {
ERROR_LOG(SCEIO, "UNTESTED 0=sceUmdActivate(%d, %s)", mode, name);
if (mode != 1) {
return hleLogError(SCEIO, 0, "UNTESTED");
}

return 0;
return hleLogSuccessI(SCEIO, 0);
}

static int sceUmdDeactivate(u32 mode, const char *name)
Expand Down Expand Up @@ -348,7 +355,7 @@ static u32 sceUmdGetDriveStat()
return retVal;
}

void __UmdStatTimeout(u64 userdata, int cyclesLate)
static void __UmdStatTimeout(u64 userdata, int cyclesLate)
{
SceUID threadID = (SceUID)userdata;

Expand Down Expand Up @@ -379,49 +386,40 @@ static void __UmdWaitStat(u32 timeout)
* @return < 0 on error
*
*/
static int sceUmdWaitDriveStat(u32 stat)
{
if (stat == 0) {
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): bad status", stat);
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
static int sceUmdWaitDriveStat(u32 stat) {
if ((stat & UMD_STAT_ALLOW_WAIT) == 0) {
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT, "bad status");
}

if (!__KernelIsDispatchEnabled()) {
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): dispatch disabled", stat);
return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
}
if (__IsInInterrupt()) {
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): inside interrupt", stat);
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "inside interrupt");
}

hleEatCycles(520);
if ((stat & __KernelUmdGetState()) == 0) {
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): waiting", stat);
umdWaitingThreads.push_back(__KernelGetCurThread());
__KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, 0, "umd stat waited");
return 0;
}

DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStat(stat = %08x)", stat);
return 0;
return hleLogSuccessI(SCEIO, 0);
}

static int sceUmdWaitDriveStatWithTimer(u32 stat, u32 timeout)
{
if (stat == 0) {
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): bad status", stat, timeout);
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
static int sceUmdWaitDriveStatWithTimer(u32 stat, u32 timeout) {
if ((stat & UMD_STAT_ALLOW_WAIT) == 0) {
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT, "bad status");
}

if (!__KernelIsDispatchEnabled()) {
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): dispatch disabled", stat, timeout);
return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
}
if (__IsInInterrupt()) {
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): inside interrupt", stat, timeout);
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "inside interrupt");
}

hleEatCycles(520);
if ((stat & __KernelUmdGetState()) == 0) {
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): waiting", stat, timeout);
__UmdWaitStat(timeout);
Expand All @@ -432,34 +430,24 @@ static int sceUmdWaitDriveStatWithTimer(u32 stat, u32 timeout)
hleReSchedule("umd stat checked");
}

DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d)", stat, timeout);
return 0;
return hleLogSuccessI(SCEIO, 0);
}

static int sceUmdWaitDriveStatCB(u32 stat, u32 timeout)
{
if (!UMDInserted) {
WARN_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): UMD is taking out for switch UMD", stat, timeout);
return PSP_UMD_NOT_PRESENT;
}

if (stat == 0) {
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): bad status", stat, timeout);
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
static int sceUmdWaitDriveStatCB(u32 stat, u32 timeout) {
if ((stat & UMD_STAT_ALLOW_WAIT) == 0) {
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT, "bad status");
}

if (!__KernelIsDispatchEnabled()) {
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): dispatch disabled", stat, timeout);
return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
}
if (__IsInInterrupt()) {
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): inside interrupt", stat, timeout);
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "inside interrupt");
}

hleEatCycles(520);
hleCheckCurrentCallbacks();
if ((stat & __KernelUmdGetState()) == 0) {
DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): waiting", stat, timeout);
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): waiting", stat, timeout);
if (timeout == 0) {
timeout = 8000;
}
Expand All @@ -471,8 +459,7 @@ static int sceUmdWaitDriveStatCB(u32 stat, u32 timeout)
hleReSchedule("umd stat waited");
}

DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStatCB(stat = %08x, timeout = %d)", stat, timeout);
return 0;
return hleLogSuccessI(SCEIO, 0);
}

static u32 sceUmdCancelWaitDriveStat()
Expand Down Expand Up @@ -503,6 +490,9 @@ void __UmdReplace(std::string filepath) {
}

UMDInserted = false;
// Wake any threads waiting for the disc to be removed.
UmdWakeThreads();

CoreTiming::ScheduleEvent(usToCycles(200*1000), umdInsertChangeEvent, 0); // Wait sceUmdCheckMedium call
// TODO Is this always correct if UMD was not activated?
u32 notifyArg = PSP_UMD_PRESENT | PSP_UMD_READABLE | PSP_UMD_CHANGED;
Expand Down

0 comments on commit 9783a2e

Please sign in to comment.