DVDInterface: Schedule event in ExecuteCommand

This commit is contained in:
JosJuice 2015-01-09 14:19:25 +01:00
parent 443d371fa3
commit af4f872dfe
6 changed files with 118 additions and 35 deletions

View file

@ -236,7 +236,7 @@ static u32 g_ErrorCode = 0;
static bool g_bDiscInside = false;
bool g_bStream = false;
static bool g_bStopAtTrackEnd = false;
static int tc = 0;
static int finish_execute_command = 0;
static int dtk = 0;
static u64 g_last_read_offset;
@ -291,7 +291,7 @@ void DoState(PointerWrap &p)
p.Do(g_bStopAtTrackEnd);
}
static void TransferComplete(u64 userdata, int cyclesLate)
static void FinishExecuteCommand(u64 userdata, int cyclesLate)
{
if (m_DICR.TSTART)
{
@ -393,7 +393,7 @@ void Init()
ejectDisc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback);
insertDisc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback);
tc = CoreTiming::RegisterEvent("TransferComplete", TransferComplete);
finish_execute_command = CoreTiming::RegisterEvent("FinishExecuteCommand", FinishExecuteCommand);
dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback);
CoreTiming::ScheduleEvent(0, dtk);
@ -541,12 +541,8 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
m_DICR.Hex = val & 7;
if (m_DICR.TSTART)
{
DVDCommandResult result = ExecuteCommand(
m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex,
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);
ExecuteCommand(m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex,
m_DIMAR.Hex, m_DILENGTH.Hex, true, finish_execute_command);
}
})
);
@ -629,8 +625,11 @@ void ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length,
result->interrupt_type = INT_TCINT;
}
DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
u32 output_address, u32 output_length, bool write_to_DIIMMBUF)
// When the command has finished executing, callback_event_type
// 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;
result.interrupt_type = INT_TCINT;
@ -1201,7 +1200,9 @@ DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
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.

View file

@ -105,7 +105,7 @@ void ChangeDisc(const std::string& fileName);
// DVD Access Functions
bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool decrypt);
extern bool g_bStream;
DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
u32 output_address, u32 output_length, bool write_to_DIIMMBUF);
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);
} // end of namespace DVDInterface

View file

@ -85,7 +85,7 @@ static u64 last_reply_time;
static const u64 ENQUEUE_REQUEST_FLAG = 0x100000000ULL;
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)
{
@ -144,7 +144,7 @@ void Init()
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++;
event_enqueue = CoreTiming::RegisterEvent("IPCEvent", EnqueueEventCallback);
event_enqueue = CoreTiming::RegisterEvent("IPCEvent", EnqueueEvent);
}
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);
}
void EnqueueReply_Immediate(u32 address)
{
EnqueueEvent(address);
}
void EnqueueCommandAcknowledgement(u32 address, int cycles_in_future)
{
CoreTiming::ScheduleEvent(cycles_in_future, event_enqueue,

View file

@ -80,6 +80,7 @@ void ExecuteCommand(u32 _Address);
void EnqueueRequest(u32 address);
void EnqueueReply(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);
} // end of namespace WII_IPC_HLE_Interface

View file

@ -9,6 +9,7 @@
#include "Common/Logging/LogManager.h"
#include "Core/ConfigManager.h"
#include "Core/CoreTiming.h"
#include "Core/VolumeHandler.h"
#include "Core/HW/DVDInterface.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_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)
{}
{
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()
{}
{
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)
{
@ -43,10 +68,29 @@ IPCCommandResult CWII_IPC_HLE_Device_di::Close(u32 _CommandAddress, bool _bForce
IPCCommandResult CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress)
{
u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10);
u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14);
u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18);
u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C);
// DI IOCtls are handled in a special way by Dolphin
// compared to other WII_IPC_HLE functions.
// This is a wrapper around DVDInterface's ExecuteCommand,
// 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_1 = Memory::Read_U32(BufferIn + 4);
@ -63,11 +107,38 @@ IPCCommandResult CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress)
Memory::Memset(BufferOut, 0, BufferOutSize);
}
DVDCommandResult result = ExecuteCommand(command_0, command_1, command_2,
BufferOut, BufferOutSize, false);
Memory::Write_U32(result.interrupt_type, _CommandAddress + 0x4);
// DVDInterface's ExecuteCommand handles most of the work.
// The IOCtl callback is used to generate a reply afterwards.
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)
@ -85,7 +156,7 @@ IPCCommandResult CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress)
u32 ReturnValue = 0;
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[2].m_Address == 0, "DVDLowOpenPartition with cert chain");

View file

@ -4,14 +4,10 @@
#pragma once
#include <deque>
#include "Core/HW/DVDInterface.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
{
public:
@ -20,9 +16,18 @@ public:
virtual ~CWII_IPC_HLE_Device_di();
void DoState(PointerWrap& p) override;
IPCCommandResult Open(u32 _CommandAddress, u32 _Mode) override;
IPCCommandResult Close(u32 _CommandAddress, bool _bForce) override;
IPCCommandResult IOCtl(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;
};