DVDInterface: Schedule event in ExecuteCommand
This commit is contained in:
parent
443d371fa3
commit
af4f872dfe
|
@ -236,7 +236,7 @@ static u32 g_ErrorCode = 0;
|
||||||
static bool g_bDiscInside = false;
|
static bool g_bDiscInside = false;
|
||||||
bool g_bStream = false;
|
bool g_bStream = false;
|
||||||
static bool g_bStopAtTrackEnd = false;
|
static bool g_bStopAtTrackEnd = false;
|
||||||
static int tc = 0;
|
static int finish_execute_command = 0;
|
||||||
static int dtk = 0;
|
static int dtk = 0;
|
||||||
|
|
||||||
static u64 g_last_read_offset;
|
static u64 g_last_read_offset;
|
||||||
|
@ -291,7 +291,7 @@ void DoState(PointerWrap &p)
|
||||||
p.Do(g_bStopAtTrackEnd);
|
p.Do(g_bStopAtTrackEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TransferComplete(u64 userdata, int cyclesLate)
|
static void FinishExecuteCommand(u64 userdata, int cyclesLate)
|
||||||
{
|
{
|
||||||
if (m_DICR.TSTART)
|
if (m_DICR.TSTART)
|
||||||
{
|
{
|
||||||
|
@ -393,7 +393,7 @@ void Init()
|
||||||
ejectDisc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback);
|
ejectDisc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback);
|
||||||
insertDisc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback);
|
insertDisc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback);
|
||||||
|
|
||||||
tc = CoreTiming::RegisterEvent("TransferComplete", TransferComplete);
|
finish_execute_command = CoreTiming::RegisterEvent("FinishExecuteCommand", FinishExecuteCommand);
|
||||||
dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback);
|
dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback);
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(0, dtk);
|
CoreTiming::ScheduleEvent(0, dtk);
|
||||||
|
@ -541,12 +541,8 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
m_DICR.Hex = val & 7;
|
m_DICR.Hex = val & 7;
|
||||||
if (m_DICR.TSTART)
|
if (m_DICR.TSTART)
|
||||||
{
|
{
|
||||||
DVDCommandResult result = ExecuteCommand(
|
ExecuteCommand(m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex,
|
||||||
m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex,
|
m_DIMAR.Hex, m_DILENGTH.Hex, true, finish_execute_command);
|
||||||
m_DIMAR.Hex, m_DILENGTH.Hex, true);
|
|
||||||
|
|
||||||
// The transfer is finished after a delay
|
|
||||||
CoreTiming::ScheduleEvent((int)result.ticks_until_completion, tc, result.interrupt_type);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -629,8 +625,11 @@ void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length,
|
||||||
result->interrupt_type = INT_TCINT;
|
result->interrupt_type = INT_TCINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
|
// When the command has finished executing, callback_event_type
|
||||||
u32 output_address, u32 output_length, bool write_to_DIIMMBUF)
|
// will be called using CoreTiming::ScheduleEvent,
|
||||||
|
// with the userdata set to the interrupt type.
|
||||||
|
void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length,
|
||||||
|
bool write_to_DIIMMBUF, int callback_event_type)
|
||||||
{
|
{
|
||||||
DVDCommandResult result;
|
DVDCommandResult result;
|
||||||
result.interrupt_type = INT_TCINT;
|
result.interrupt_type = INT_TCINT;
|
||||||
|
@ -1201,7 +1200,9 @@ DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
// The command will finish executing after a delay,
|
||||||
|
// to simulate the speed of a real disc drive
|
||||||
|
CoreTiming::ScheduleEvent((int)result.ticks_until_completion, callback_event_type, result.interrupt_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simulates the timing aspects of reading data from a disc.
|
// Simulates the timing aspects of reading data from a disc.
|
||||||
|
|
|
@ -105,7 +105,7 @@ void ChangeDisc(const std::string& fileName);
|
||||||
// DVD Access Functions
|
// DVD Access Functions
|
||||||
bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool decrypt);
|
bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool decrypt);
|
||||||
extern bool g_bStream;
|
extern bool g_bStream;
|
||||||
DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
|
void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length,
|
||||||
u32 output_address, u32 output_length, bool write_to_DIIMMBUF);
|
bool write_to_DIIMMBUF, int callback_event_type);
|
||||||
|
|
||||||
} // end of namespace DVDInterface
|
} // end of namespace DVDInterface
|
||||||
|
|
|
@ -85,7 +85,7 @@ static u64 last_reply_time;
|
||||||
|
|
||||||
static const u64 ENQUEUE_REQUEST_FLAG = 0x100000000ULL;
|
static const u64 ENQUEUE_REQUEST_FLAG = 0x100000000ULL;
|
||||||
static const u64 ENQUEUE_ACKNOWLEDGEMENT_FLAG = 0x200000000ULL;
|
static const u64 ENQUEUE_ACKNOWLEDGEMENT_FLAG = 0x200000000ULL;
|
||||||
static void EnqueueEventCallback(u64 userdata, int)
|
static void EnqueueEvent(u64 userdata, int cycles_late = 0)
|
||||||
{
|
{
|
||||||
if (userdata & ENQUEUE_ACKNOWLEDGEMENT_FLAG)
|
if (userdata & ENQUEUE_ACKNOWLEDGEMENT_FLAG)
|
||||||
{
|
{
|
||||||
|
@ -144,7 +144,7 @@ void Init()
|
||||||
g_DeviceMap[i] = new CWII_IPC_HLE_Device_stub(i, "/dev/usb/oh1"); i++;
|
g_DeviceMap[i] = new CWII_IPC_HLE_Device_stub(i, "/dev/usb/oh1"); i++;
|
||||||
g_DeviceMap[i] = new IWII_IPC_HLE_Device(i, "_Unimplemented_Device_"); i++;
|
g_DeviceMap[i] = new IWII_IPC_HLE_Device(i, "_Unimplemented_Device_"); i++;
|
||||||
|
|
||||||
event_enqueue = CoreTiming::RegisterEvent("IPCEvent", EnqueueEventCallback);
|
event_enqueue = CoreTiming::RegisterEvent("IPCEvent", EnqueueEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset(bool _bHard)
|
void Reset(bool _bHard)
|
||||||
|
@ -563,6 +563,11 @@ void EnqueueReply_Threadsafe(u32 address, int cycles_in_future)
|
||||||
CoreTiming::ScheduleEvent_Threadsafe(cycles_in_future, event_enqueue, address);
|
CoreTiming::ScheduleEvent_Threadsafe(cycles_in_future, event_enqueue, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EnqueueReply_Immediate(u32 address)
|
||||||
|
{
|
||||||
|
EnqueueEvent(address);
|
||||||
|
}
|
||||||
|
|
||||||
void EnqueueCommandAcknowledgement(u32 address, int cycles_in_future)
|
void EnqueueCommandAcknowledgement(u32 address, int cycles_in_future)
|
||||||
{
|
{
|
||||||
CoreTiming::ScheduleEvent(cycles_in_future, event_enqueue,
|
CoreTiming::ScheduleEvent(cycles_in_future, event_enqueue,
|
||||||
|
|
|
@ -80,6 +80,7 @@ void ExecuteCommand(u32 _Address);
|
||||||
void EnqueueRequest(u32 address);
|
void EnqueueRequest(u32 address);
|
||||||
void EnqueueReply(u32 address, int cycles_in_future = 0);
|
void EnqueueReply(u32 address, int cycles_in_future = 0);
|
||||||
void EnqueueReply_Threadsafe(u32 address, int cycles_in_future = 0);
|
void EnqueueReply_Threadsafe(u32 address, int cycles_in_future = 0);
|
||||||
|
void EnqueueReply_Immediate(u32 address);
|
||||||
void EnqueueCommandAcknowledgement(u32 _Address, int cycles_in_future = 0);
|
void EnqueueCommandAcknowledgement(u32 _Address, int cycles_in_future = 0);
|
||||||
|
|
||||||
} // end of namespace WII_IPC_HLE_Interface
|
} // end of namespace WII_IPC_HLE_Interface
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "Common/Logging/LogManager.h"
|
#include "Common/Logging/LogManager.h"
|
||||||
|
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/VolumeHandler.h"
|
#include "Core/VolumeHandler.h"
|
||||||
#include "Core/HW/DVDInterface.h"
|
#include "Core/HW/DVDInterface.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
|
@ -17,14 +18,38 @@
|
||||||
#include "Core/IPC_HLE/WII_IPC_HLE.h"
|
#include "Core/IPC_HLE/WII_IPC_HLE.h"
|
||||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_DI.h"
|
#include "Core/IPC_HLE/WII_IPC_HLE_Device_DI.h"
|
||||||
|
|
||||||
using namespace DVDInterface;
|
static CWII_IPC_HLE_Device_di* g_di_pointer;
|
||||||
|
static int ioctl_callback;
|
||||||
|
|
||||||
CWII_IPC_HLE_Device_di::CWII_IPC_HLE_Device_di(u32 _DeviceID, const std::string& _rDeviceName )
|
static void IOCtlCallback(u64 userdata, int cycles_late)
|
||||||
|
{
|
||||||
|
if (g_di_pointer != nullptr)
|
||||||
|
g_di_pointer->FinishIOCtl((DVDInterface::DIInterruptType)userdata);
|
||||||
|
|
||||||
|
// If g_di_pointer == nullptr, IOS was probably shut down,
|
||||||
|
// so the command shouldn't be completed
|
||||||
|
}
|
||||||
|
|
||||||
|
CWII_IPC_HLE_Device_di::CWII_IPC_HLE_Device_di(u32 _DeviceID, const std::string& _rDeviceName)
|
||||||
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
|
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
|
||||||
{}
|
{
|
||||||
|
if (g_di_pointer == nullptr)
|
||||||
|
ERROR_LOG(WII_IPC_DVD, "Trying to run two DI devices at once. IOCtl may not behave as expected.");
|
||||||
|
|
||||||
|
g_di_pointer = this;
|
||||||
|
ioctl_callback = CoreTiming::RegisterEvent("IOCtlCallbackDI", IOCtlCallback);
|
||||||
|
}
|
||||||
|
|
||||||
CWII_IPC_HLE_Device_di::~CWII_IPC_HLE_Device_di()
|
CWII_IPC_HLE_Device_di::~CWII_IPC_HLE_Device_di()
|
||||||
{}
|
{
|
||||||
|
g_di_pointer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWII_IPC_HLE_Device_di::DoState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
DoStateShared(p);
|
||||||
|
p.Do(m_commands_to_execute);
|
||||||
|
}
|
||||||
|
|
||||||
IPCCommandResult CWII_IPC_HLE_Device_di::Open(u32 _CommandAddress, u32 _Mode)
|
IPCCommandResult CWII_IPC_HLE_Device_di::Open(u32 _CommandAddress, u32 _Mode)
|
||||||
{
|
{
|
||||||
|
@ -43,10 +68,29 @@ IPCCommandResult CWII_IPC_HLE_Device_di::Close(u32 _CommandAddress, bool _bForce
|
||||||
|
|
||||||
IPCCommandResult CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress)
|
IPCCommandResult CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress)
|
||||||
{
|
{
|
||||||
u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10);
|
// DI IOCtls are handled in a special way by Dolphin
|
||||||
u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14);
|
// compared to other WII_IPC_HLE functions.
|
||||||
u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18);
|
// This is a wrapper around DVDInterface's ExecuteCommand,
|
||||||
u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C);
|
// which will execute commands more or less asynchronously.
|
||||||
|
// Only one command can be executed at a time, so commands
|
||||||
|
// are queued until DVDInterface is ready to handle them.
|
||||||
|
|
||||||
|
bool ready_to_execute = m_commands_to_execute.empty();
|
||||||
|
m_commands_to_execute.push_back(_CommandAddress);
|
||||||
|
if (ready_to_execute)
|
||||||
|
StartIOCtl(_CommandAddress);
|
||||||
|
|
||||||
|
// DVDInterface handles the timing, and we handle the reply,
|
||||||
|
// so WII_IPC_HLE shouldn't do any of that.
|
||||||
|
return IPC_NO_REPLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWII_IPC_HLE_Device_di::StartIOCtl(u32 command_address)
|
||||||
|
{
|
||||||
|
u32 BufferIn = Memory::Read_U32(command_address + 0x10);
|
||||||
|
u32 BufferInSize = Memory::Read_U32(command_address + 0x14);
|
||||||
|
u32 BufferOut = Memory::Read_U32(command_address + 0x18);
|
||||||
|
u32 BufferOutSize = Memory::Read_U32(command_address + 0x1C);
|
||||||
|
|
||||||
u32 command_0 = Memory::Read_U32(BufferIn);
|
u32 command_0 = Memory::Read_U32(BufferIn);
|
||||||
u32 command_1 = Memory::Read_U32(BufferIn + 4);
|
u32 command_1 = Memory::Read_U32(BufferIn + 4);
|
||||||
|
@ -63,11 +107,38 @@ IPCCommandResult CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress)
|
||||||
Memory::Memset(BufferOut, 0, BufferOutSize);
|
Memory::Memset(BufferOut, 0, BufferOutSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
DVDCommandResult result = ExecuteCommand(command_0, command_1, command_2,
|
// DVDInterface's ExecuteCommand handles most of the work.
|
||||||
BufferOut, BufferOutSize, false);
|
// The IOCtl callback is used to generate a reply afterwards.
|
||||||
Memory::Write_U32(result.interrupt_type, _CommandAddress + 0x4);
|
DVDInterface::ExecuteCommand(command_0, command_1, command_2, BufferOut, BufferOutSize,
|
||||||
|
false, ioctl_callback);
|
||||||
|
}
|
||||||
|
|
||||||
return { true, result.ticks_until_completion };
|
void CWII_IPC_HLE_Device_di::FinishIOCtl(DVDInterface::DIInterruptType interrupt_type)
|
||||||
|
{
|
||||||
|
if (m_commands_to_execute.empty())
|
||||||
|
{
|
||||||
|
PanicAlertT("WII_IPC_HLE_Device_DI tried to reply to non-existing command");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This command has been executed, so it's removed from the queue
|
||||||
|
u32 command_address = m_commands_to_execute.front();
|
||||||
|
m_commands_to_execute.pop_front();
|
||||||
|
|
||||||
|
// The DI interrupt type is used as a return value
|
||||||
|
Memory::Write_U32(interrupt_type, command_address + 4);
|
||||||
|
|
||||||
|
// The original hardware overwrites the command type with the async reply type.
|
||||||
|
Memory::Write_U32(IPC_REP_ASYNC, command_address);
|
||||||
|
// IOS also seems to write back the command that was responded to in the FD field.
|
||||||
|
Memory::Write_U32(Memory::Read_U32(command_address), command_address + 8);
|
||||||
|
// Generate a reply to the IPC command
|
||||||
|
WII_IPC_HLE_Interface::EnqueueReply_Immediate(command_address);
|
||||||
|
|
||||||
|
// DVDInterface is now ready to execute another command,
|
||||||
|
// so we start executing a command from the queue if there is one
|
||||||
|
if (!m_commands_to_execute.empty())
|
||||||
|
StartIOCtl(m_commands_to_execute.front());
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCCommandResult CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress)
|
IPCCommandResult CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress)
|
||||||
|
@ -85,7 +156,7 @@ IPCCommandResult CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress)
|
||||||
u32 ReturnValue = 0;
|
u32 ReturnValue = 0;
|
||||||
switch (CommandBuffer.Parameter)
|
switch (CommandBuffer.Parameter)
|
||||||
{
|
{
|
||||||
case DVDLowOpenPartition:
|
case DVDInterface::DVDLowOpenPartition:
|
||||||
{
|
{
|
||||||
_dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[1].m_Address == 0, "DVDLowOpenPartition with ticket");
|
_dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[1].m_Address == 0, "DVDLowOpenPartition with ticket");
|
||||||
_dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[2].m_Address == 0, "DVDLowOpenPartition with cert chain");
|
_dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[2].m_Address == 0, "DVDLowOpenPartition with cert chain");
|
||||||
|
|
|
@ -4,14 +4,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include "Core/HW/DVDInterface.h"
|
||||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device.h"
|
#include "Core/IPC_HLE/WII_IPC_HLE_Device.h"
|
||||||
|
|
||||||
namespace DiscIO
|
|
||||||
{
|
|
||||||
class IVolume;
|
|
||||||
class IFileSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CWII_IPC_HLE_Device_di : public IWII_IPC_HLE_Device
|
class CWII_IPC_HLE_Device_di : public IWII_IPC_HLE_Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -20,9 +16,18 @@ public:
|
||||||
|
|
||||||
virtual ~CWII_IPC_HLE_Device_di();
|
virtual ~CWII_IPC_HLE_Device_di();
|
||||||
|
|
||||||
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
IPCCommandResult Open(u32 _CommandAddress, u32 _Mode) override;
|
IPCCommandResult Open(u32 _CommandAddress, u32 _Mode) override;
|
||||||
IPCCommandResult Close(u32 _CommandAddress, bool _bForce) override;
|
IPCCommandResult Close(u32 _CommandAddress, bool _bForce) override;
|
||||||
|
|
||||||
IPCCommandResult IOCtl(u32 _CommandAddress) override;
|
IPCCommandResult IOCtl(u32 _CommandAddress) override;
|
||||||
IPCCommandResult IOCtlV(u32 _CommandAddress) override;
|
IPCCommandResult IOCtlV(u32 _CommandAddress) override;
|
||||||
|
|
||||||
|
void FinishIOCtl(DVDInterface::DIInterruptType interrupt_type);
|
||||||
|
private:
|
||||||
|
|
||||||
|
void StartIOCtl(u32 command_address);
|
||||||
|
|
||||||
|
std::deque<u32> m_commands_to_execute;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue