make WII_IPC_HLEInterface manage the IPC message queue instead of the "lle" portion.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5224 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Shawn Hoffman 2010-03-23 03:32:19 +00:00
parent cf7101ef08
commit f6db5b7d8d
7 changed files with 165 additions and 281 deletions

View file

@ -16,6 +16,8 @@
// http://code.google.com/p/dolphin-emu/ // http://code.google.com/p/dolphin-emu/
#include <map> #include <map>
#include <queue>
#include <vector>
#include "Common.h" #include "Common.h"
#include "ChunkFile.h" #include "ChunkFile.h"
@ -91,23 +93,16 @@ struct CtrlRegister
}; };
// STATE_TO_SAVE // STATE_TO_SAVE
u32 ppc_msg; static u32 ppc_msg;
u32 arm_msg; static u32 arm_msg;
CtrlRegister ctrl; static CtrlRegister ctrl;
u32 ppc_irq_flags; static u32 ppc_irq_flags;
u32 ppc_irq_masks; static u32 ppc_irq_masks;
u32 arm_irq_flags; static u32 arm_irq_flags;
u32 arm_irq_masks; static u32 arm_irq_masks;
u32 sensorbar_power; // do we need to care about this? static u32 sensorbar_power; // do we need to care about this?
// not actual registers:
bool cmd_active;
u32 g_ReplyHead;
u32 g_ReplyTail;
u32 g_ReplyNum;
u32 g_ReplyFifo[REPLY_FIFO_DEPTH];
void DoState(PointerWrap &p) void DoState(PointerWrap &p)
{ {
@ -119,17 +114,10 @@ void DoState(PointerWrap &p)
p.Do(arm_irq_flags); p.Do(arm_irq_flags);
p.Do(arm_irq_masks); p.Do(arm_irq_masks);
p.Do(sensorbar_power); p.Do(sensorbar_power);
p.Do(cmd_active);
p.Do(g_ReplyHead);
p.Do(g_ReplyTail);
p.Do(g_ReplyNum);
p.DoArray(g_ReplyFifo, REPLY_FIFO_DEPTH);
} }
// Init
void Init() void Init()
{ {
cmd_active = false;
ctrl = CtrlRegister(); ctrl = CtrlRegister();
ppc_msg = ppc_msg =
arm_msg = arm_msg =
@ -139,12 +127,7 @@ void Init()
arm_irq_flags = arm_irq_flags =
arm_irq_masks = arm_irq_masks =
sensorbar_power = sensorbar_power = 0;
g_ReplyHead =
g_ReplyTail =
g_ReplyNum = 0;
memset(g_ReplyFifo, 0, REPLY_FIFO_DEPTH);
ppc_irq_masks |= INT_CAUSE_IPC_BROADWAY; ppc_irq_masks |= INT_CAUSE_IPC_BROADWAY;
} }
@ -166,7 +149,7 @@ void Read32(u32& _rReturnValue, const u32 _Address)
{ {
case IPC_PPCCTRL: case IPC_PPCCTRL:
_rReturnValue = ctrl.ppc(); _rReturnValue = ctrl.ppc();
INFO_LOG(WII_IPC, "r32 IPC_PPCCTRL %03x [R:%i A:%i E:%i]", DEBUG_LOG(WII_IPC, "r32 IPC_PPCCTRL %03x [R:%i A:%i E:%i]",
ctrl.ppc(), ctrl.Y1, ctrl.Y2, ctrl.X1); ctrl.ppc(), ctrl.Y1, ctrl.Y2, ctrl.X1);
// if ((REASON_REG & 0x14) == 0x14) CALL IPCReplayHandler // if ((REASON_REG & 0x14) == 0x14) CALL IPCReplayHandler
@ -175,7 +158,7 @@ void Read32(u32& _rReturnValue, const u32 _Address)
case IPC_ARMMSG: // looks a little bit like a callback function case IPC_ARMMSG: // looks a little bit like a callback function
_rReturnValue = arm_msg; _rReturnValue = arm_msg;
INFO_LOG(WII_IPC, "r32 IPC_ARMMSG %08x ", _rReturnValue); DEBUG_LOG(WII_IPC, "r32 IPC_ARMMSG %08x ", _rReturnValue);
break; break;
case GPIOB_OUT: case GPIOB_OUT:
@ -195,24 +178,28 @@ void Write32(const u32 _Value, const u32 _Address)
case IPC_PPCMSG: // __ios_Ipc2 ... a value from __responses is loaded case IPC_PPCMSG: // __ios_Ipc2 ... a value from __responses is loaded
{ {
ppc_msg = _Value; ppc_msg = _Value;
INFO_LOG(WII_IPC, "IPC_PPCMSG = %08x", ppc_msg); DEBUG_LOG(WII_IPC, "IPC_PPCMSG = %08x", ppc_msg);
} }
break; break;
case IPC_PPCCTRL: case IPC_PPCCTRL:
{ {
ctrl.ppc(_Value); ctrl.ppc(_Value);
INFO_LOG(WII_IPC, "w32 %08x IPC_PPCCTRL = %03x [R:%i A:%i E:%i]", DEBUG_LOG(WII_IPC, "w32 %08x IPC_PPCCTRL = %03x [R:%i A:%i E:%i]",
_Value, ctrl.ppc(), ctrl.Y1, ctrl.Y2, ctrl.X1); _Value, ctrl.ppc(), ctrl.Y1, ctrl.Y2, ctrl.X1);
if (ctrl.X1) // seems like we should just be able to use X1 directly, but it doesn't work...why?! if (ctrl.X1)
cmd_active = true; {
INFO_LOG(WII_IPC, "new pointer available: %08x", ppc_msg);
// Let the HLE handle the request on it's own time
WII_IPC_HLE_Interface::EnqRequest(ppc_msg);
}
} }
break; break;
case PPC_IRQFLAG: // ACR REGISTER IT IS CALLED IN DEBUG case PPC_IRQFLAG: // ACR REGISTER IT IS CALLED IN DEBUG
{ {
ppc_irq_flags &= ~_Value; ppc_irq_flags &= ~_Value;
INFO_LOG(WII_IPC, "w32 PPC_IRQFLAG %08x (%08x)", _Value, ppc_irq_flags); DEBUG_LOG(WII_IPC, "w32 PPC_IRQFLAG %08x (%08x)", _Value, ppc_irq_flags);
} }
break; break;
@ -221,7 +208,7 @@ void Write32(const u32 _Value, const u32 _Address)
ppc_irq_masks = _Value; ppc_irq_masks = _Value;
if (ppc_irq_masks & INT_CAUSE_IPC_BROADWAY) // wtf? if (ppc_irq_masks & INT_CAUSE_IPC_BROADWAY) // wtf?
Reset(); Reset();
INFO_LOG(WII_IPC, "w32 PPC_IRQMASK %08x", ppc_irq_masks); DEBUG_LOG(WII_IPC, "w32 PPC_IRQMASK %08x", ppc_irq_masks);
} }
break; break;
@ -253,21 +240,12 @@ void UpdateInterrupts()
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_WII_IPC, !!(ppc_irq_flags & ppc_irq_masks)); ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_WII_IPC, !!(ppc_irq_flags & ppc_irq_masks));
} }
// The rest is for IOS HLE void GenerateAck(u32 _Address)
bool IsReady()
{ {
return ((ctrl.Y1 == 0) && (ctrl.Y2 == 0) && ((ppc_irq_flags & INT_CAUSE_IPC_BROADWAY) == 0)); arm_msg = _Address; // dunno if it's really set here, but HLE needs to stay in context
}
u32 GetAddress()
{
return (cmd_active ? ppc_msg : 0);
}
void GenerateAck()
{
cmd_active = false;
ctrl.Y2 = 1; ctrl.Y2 = 1;
INFO_LOG(WII_IPC, "GenerateAck: %08x | %08x [R:%i A:%i E:%i]",
ppc_msg,_Address, ctrl.Y1, ctrl.Y2, ctrl.X1);
UpdateInterrupts(); UpdateInterrupts();
} }
@ -275,40 +253,14 @@ void GenerateReply(u32 _Address)
{ {
arm_msg = _Address; arm_msg = _Address;
ctrl.Y1 = 1; ctrl.Y1 = 1;
INFO_LOG(WII_IPC, "GenerateReply: %08x | %08x [R:%i A:%i E:%i]",
ppc_msg,_Address, ctrl.Y1, ctrl.Y2, ctrl.X1);
UpdateInterrupts(); UpdateInterrupts();
} }
void EnqReply(u32 _Address) bool IsReady()
{ {
if (g_ReplyNum < REPLY_FIFO_DEPTH) return ((ctrl.Y1 == 0) && (ctrl.Y2 == 0) && ((ppc_irq_flags & INT_CAUSE_IPC_BROADWAY) == 0));
{
g_ReplyFifo[g_ReplyTail++] = _Address;
g_ReplyTail &= REPLY_FIFO_MASK;
g_ReplyNum++;
}
else
{
ERROR_LOG(WII_IPC, "Reply FIFO is full, something must be wrong!");
PanicAlert("WII_IPC: Reply FIFO is full, something must be wrong!");
}
}
u32 DeqReply()
{
u32 _Address;
if (g_ReplyNum)
{
_Address = g_ReplyFifo[g_ReplyHead++];
g_ReplyHead &= REPLY_FIFO_MASK;
g_ReplyNum--;
}
else
{
_Address = NULL;
}
return _Address;
} }
} }

View file

@ -44,9 +44,6 @@ enum StarletInterruptCause
INT_CAUSE_IPC_STARLET = 0x80000000 INT_CAUSE_IPC_STARLET = 0x80000000
}; };
#define REPLY_FIFO_DEPTH (unsigned)8
#define REPLY_FIFO_MASK (REPLY_FIFO_DEPTH - 1)
void Init(); void Init();
void Reset(); void Reset();
void Shutdown(); void Shutdown();
@ -55,14 +52,10 @@ void DoState(PointerWrap &p);
void Read32(u32& _rReturnValue, const u32 _Address); void Read32(u32& _rReturnValue, const u32 _Address);
void Write32(const u32 _Value, const u32 _Address); void Write32(const u32 _Value, const u32 _Address);
u32 GetAddress();
void GenerateAck();
void GenerateReply(u32 _Address);
void InsertReply(u32 _Address);
void EnqReply(u32 _Address);
u32 DeqReply();
void UpdateInterrupts(); void UpdateInterrupts();
void GenerateAck(u32 _Address);
void GenerateReply(u32 _Address);
bool IsReady(); bool IsReady();
} }

View file

@ -15,18 +15,14 @@
// Official SVN repository and contact information can be found at // Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/ // http://code.google.com/p/dolphin-emu/
/*
This is the main Wii IPC file that handles all incoming IPC calls and directs them
to the right function.
IPC basics (IOS' usage):
// ======================================================= Return values for file handles: All IPC calls will generate a return value to 0x04,
// File description in case of success they are
// -------------
/* This is the main Wii IPC file that handles all incoming IPC calls and directs them
to the right function.
IPC basics:
Return values for file handles: All IPC calls will generate a return value to 0x04,
in case of success they are
Open: DeviceID Open: DeviceID
Close: 0 Close: 0
Read: Bytes read Read: Bytes read
@ -34,11 +30,8 @@
Seek: Seek position Seek: Seek position
Ioctl: 0 (in addition to that there may be messages to the out buffers) Ioctl: 0 (in addition to that there may be messages to the out buffers)
Ioctlv: 0 (in addition to that there may be messages to the out buffers) Ioctlv: 0 (in addition to that there may be messages to the out buffers)
They will also generate a true or false return for UpdateInterrupts() in WII_IPC.cpp. */ They will also generate a true or false return for UpdateInterrupts() in WII_IPC.cpp.
// ============= */
#include <map> #include <map>
#include <string> #include <string>
@ -78,6 +71,10 @@ typedef std::map<u32, std::string> TFileNameMap;
TFileNameMap g_FileNameMap; TFileNameMap g_FileNameMap;
u32 g_LastDeviceID; u32 g_LastDeviceID;
typedef std::queue<u32> ipc_msg_queue;
static ipc_msg_queue request_queue; // ppc -> arm
static ipc_msg_queue reply_queue; // arm -> ppc
// General IPC functions // General IPC functions
void Init() void Init()
{ {
@ -125,6 +122,9 @@ void Reset(bool _bHard)
g_DeviceMap.erase(itr, g_DeviceMap.end()); g_DeviceMap.erase(itr, g_DeviceMap.end());
g_FileNameMap.clear(); g_FileNameMap.clear();
request_queue = std::queue<u32>();
reply_queue = std::queue<u32>();
g_LastDeviceID = IPC_FIRST_FILEIO_ID; g_LastDeviceID = IPC_FIRST_FILEIO_ID;
} }
@ -133,7 +133,6 @@ void Shutdown()
Reset(true); Reset(true);
} }
// Set default content file
void SetDefaultContentFile(const std::string& _rFilename) void SetDefaultContentFile(const std::string& _rFilename)
{ {
CWII_IPC_HLE_Device_es* pDevice = (CWII_IPC_HLE_Device_es*)AccessDeviceByID(GetDeviceIDByName(std::string("/dev/es"))); CWII_IPC_HLE_Device_es* pDevice = (CWII_IPC_HLE_Device_es*)AccessDeviceByID(GetDeviceIDByName(std::string("/dev/es")));
@ -172,7 +171,7 @@ void DeleteDeviceByID(u32 ID)
g_FileNameMap.erase(ID); g_FileNameMap.erase(ID);
} }
// This is called from COMMAND_OPEN_DEVICE. Here we either create a new file handle // This is called from ExecuteCommand() COMMAND_OPEN_DEVICE
IWII_IPC_HLE_Device* CreateFileIO(u32 _DeviceID, const std::string& _rDeviceName) IWII_IPC_HLE_Device* CreateFileIO(u32 _DeviceID, const std::string& _rDeviceName)
{ {
// scan device name and create the right one // scan device name and create the right one
@ -184,7 +183,7 @@ IWII_IPC_HLE_Device* CreateFileIO(u32 _DeviceID, const std::string& _rDeviceName
return pDevice; return pDevice;
} }
// Let the game read the setting.txt file // Try to make sure the game is always reading the setting.txt file we provide
void CopySettingsFile(std::string& DeviceName) void CopySettingsFile(std::string& DeviceName)
{ {
std::string Source = File::GetSysDirectory() + WII_SYS_DIR + DIR_SEP; std::string Source = File::GetSysDirectory() + WII_SYS_DIR + DIR_SEP;
@ -197,7 +196,8 @@ void CopySettingsFile(std::string& DeviceName)
// Check if the target dir exists, otherwise create it // Check if the target dir exists, otherwise create it
std::string TargetDir = Target.substr(0, Target.find_last_of(DIR_SEP)); std::string TargetDir = Target.substr(0, Target.find_last_of(DIR_SEP));
if(!File::IsDirectory(TargetDir.c_str())) File::CreateFullPath(Target.c_str()); if (!File::IsDirectory(TargetDir.c_str()))
File::CreateFullPath(Target.c_str());
if (File::Copy(Source.c_str(), Target.c_str())) if (File::Copy(Source.c_str(), Target.c_str()))
{ {
@ -256,14 +256,6 @@ void DoState(PointerWrap &p)
} }
} }
// ===================================================
/* This generates an acknowledgment to IPC calls. This function is called from
IPC_CONTROL_REGISTER requests in WII_IPC.cpp. The acknowledgment _Address will
start with 0x033e...., it will be for the _CommandAddress 0x133e...., from
debugging I also noticed that the Ioctl arguments are stored temporarily in
0x933e.... with the same .... as in the _CommandAddress. */
// ----------------
void ExecuteCommand(u32 _Address) void ExecuteCommand(u32 _Address)
{ {
bool CmdSuccess = false; bool CmdSuccess = false;
@ -284,7 +276,8 @@ void ExecuteCommand(u32 _Address)
Memory::GetString(DeviceName, Memory::Read_U32(_Address + 0xC)); Memory::GetString(DeviceName, Memory::Read_U32(_Address + 0xC));
// The game may try to read setting.txt here, in that case copy it so it can read it // The game may try to read setting.txt here, in that case copy it so it can read it
if(DeviceName.find("setting.txt") != std::string::npos) CopySettingsFile(DeviceName); if (DeviceName.find("setting.txt") != std::string::npos)
CopySettingsFile(DeviceName);
u32 Mode = Memory::Read_U32(_Address + 0x10); u32 Mode = Memory::Read_U32(_Address + 0x10);
DeviceID = GetDeviceIDByName(DeviceName); DeviceID = GetDeviceIDByName(DeviceName);
@ -332,9 +325,9 @@ void ExecuteCommand(u32 _Address)
INFO_LOG(WII_IPC_FILEIO, "IOP: ReOpen (Device=%s, DeviceID=%08x, Mode=%i)", INFO_LOG(WII_IPC_FILEIO, "IOP: ReOpen (Device=%s, DeviceID=%08x, Mode=%i)",
pDevice->GetDeviceName().c_str(), DeviceID, Mode); pDevice->GetDeviceName().c_str(), DeviceID, Mode);
if(pDevice->IsHardware()) if (pDevice->IsHardware())
{ {
if(pDevice->IsOpened()) if (pDevice->IsOpened())
{ {
if (pDevice->GetDeviceName().find("/dev/net/kd/request") != std::string::npos) if (pDevice->GetDeviceName().find("/dev/net/kd/request") != std::string::npos)
// AyuanX: /dev/net/kd/request is more like event which doesn't need close so it can be reopened // AyuanX: /dev/net/kd/request is more like event which doesn't need close so it can be reopened
@ -358,7 +351,7 @@ void ExecuteCommand(u32 _Address)
// Open > Failed > ... other stuff > ReOpen call sequence, in that case // Open > Failed > ... other stuff > ReOpen call sequence, in that case
// we have no file and no file handle, so we call Open again to basically // we have no file and no file handle, so we call Open again to basically
// get a -106 error so that the game call CreateFile and then ReOpen again. // get a -106 error so that the game call CreateFile and then ReOpen again.
if(pDevice->ReturnFileHandle()) if (pDevice->ReturnFileHandle())
Memory::Write_U32(DeviceID, _Address + 4); Memory::Write_U32(DeviceID, _Address + 4);
else else
pDevice->Open(_Address, Mode); pDevice->Open(_Address, Mode);
@ -368,7 +361,6 @@ void ExecuteCommand(u32 _Address)
break; break;
case COMMAND_CLOSE_DEVICE: case COMMAND_CLOSE_DEVICE:
{
if (pDevice) if (pDevice)
{ {
pDevice->Close(_Address); pDevice->Close(_Address);
@ -377,101 +369,94 @@ void ExecuteCommand(u32 _Address)
DeleteDeviceByID(DeviceID); DeleteDeviceByID(DeviceID);
CmdSuccess = true; CmdSuccess = true;
} }
}
break; break;
case COMMAND_READ: case COMMAND_READ:
{
if (pDevice != NULL) if (pDevice != NULL)
CmdSuccess = pDevice->Read(_Address); CmdSuccess = pDevice->Read(_Address);
}
break; break;
case COMMAND_WRITE: case COMMAND_WRITE:
{
if (pDevice != NULL) if (pDevice != NULL)
CmdSuccess = pDevice->Write(_Address); CmdSuccess = pDevice->Write(_Address);
}
break; break;
case COMMAND_SEEK: case COMMAND_SEEK:
{
if (pDevice != NULL) if (pDevice != NULL)
CmdSuccess = pDevice->Seek(_Address); CmdSuccess = pDevice->Seek(_Address);
}
break; break;
case COMMAND_IOCTL: case COMMAND_IOCTL:
{
if (pDevice != NULL) if (pDevice != NULL)
CmdSuccess = pDevice->IOCtl(_Address); CmdSuccess = pDevice->IOCtl(_Address);
}
break; break;
case COMMAND_IOCTLV: case COMMAND_IOCTLV:
{
if (pDevice) if (pDevice)
CmdSuccess = pDevice->IOCtlV(_Address); CmdSuccess = pDevice->IOCtlV(_Address);
}
break; break;
default: default:
_dbg_assert_msg_(WII_IPC_HLE, 0, "Unknown IPC Command %i (0x%08x)", Command, _Address); _dbg_assert_msg_(WII_IPC_HLE, 0, "Unknown IPC Command %i (0x%08x)", Command, _Address);
// Break on the same terms as the _dbg_assert_msg_, if LOGGING was defined
break; break;
} }
// It seems that the original hardware overwrites the command after it has been // It seems that the original hardware overwrites the command after it has been
// executed. We write 8 which is not any valid command. // executed. We write 8 which is not any valid command, and what IOS does
// Memory::Write_U32(8, _Address);
// AyuanX: Is this really necessary? // IOS seems to write back the command that was responded to
// My experiment says no, so I'm just commenting this out Memory::Write_U32(Command, _Address + 8);
//
//Memory::Write_U32(8, _Address);
if (CmdSuccess) if (CmdSuccess)
{ {
// Generate a reply to the IPC command // Generate a reply to the IPC command
WII_IPCInterface::EnqReply(_Address); EnqReply(_Address);
} }
else else
{ {
//INFO_LOG(WII_IPC_HLE, "<<-- Failed or Not Ready to Reply to Command Address: 0x%08x ", _Address); ERROR_LOG(WII_IPC_HLE, "<<-- Failed or Not Ready to Reply to IPC Request @ 0x%08x ", _Address);
} }
} }
// Happens AS SOON AS IPC gets a new pointer!
void EnqRequest(u32 _Address)
{
request_queue.push(_Address);
}
// =================================================== // Called when IOS module has some reply
// This is called continuously from SystemTimers.cpp void EnqReply(u32 _Address)
// --------------------------------------------------- {
reply_queue.push(_Address);
}
// This is called every IPC_HLE_PERIOD from SystemTimers.cpp
// Takes care of routing ipc <-> ipc HLE
void Update() void Update()
{ {
if (WII_IPCInterface::IsReady() == false) if (!WII_IPCInterface::IsReady())
return; return;
UpdateDevices(); UpdateDevices();
// if we have a reply to send if (reply_queue.size())
u32 _Reply = WII_IPCInterface::DeqReply();
if (_Reply)
{ {
WII_IPCInterface::GenerateReply(_Reply); WII_IPCInterface::GenerateReply(reply_queue.front());
INFO_LOG(WII_IPC_HLE, "<<-- Reply to Command Address: 0x%08x", _Reply); INFO_LOG(WII_IPC_HLE, "<<-- Reply to IPC Request @ 0x%08x", reply_queue.front());
reply_queue.pop();
return; return;
} }
// If there is a a new command if (request_queue.size())
u32 _Address = WII_IPCInterface::GetAddress();
if (_Address)
{ {
WII_IPCInterface::GenerateAck(); WII_IPCInterface::GenerateAck(request_queue.front());
INFO_LOG(WII_IPC_HLE, "||-- Acknowledge Command Address: 0x%08x", _Address); INFO_LOG(WII_IPC_HLE, "||-- Acknowledge IPC Request @ 0x%08x", request_queue.front());
ExecuteCommand(_Address); ExecuteCommand(request_queue.front());
request_queue.pop();
#if MAX_LOG_LEVEL >= DEBUG_LEVEL #if MAX_LOGLEVEL >= DEBUG_LEVEL
Debugger::PrintCallstack(LogTypes::WII_IPC_HLE, LogTypes::LDEBUG); Dolphin_Debugger::PrintCallstack(LogTypes::WII_IPC_HLE, LogTypes::LDEBUG);
#endif #endif
} }
} }
@ -488,4 +473,4 @@ void UpdateDevices()
} }
} // end of namespace IPC } // end of namespace WII_IPC_HLE_Interface

View file

@ -61,6 +61,9 @@ void UpdateDevices();
void ExecuteCommand(u32 _Address); void ExecuteCommand(u32 _Address);
void EnqRequest(u32 _Address);
void EnqReply(u32 _Address);
enum ECommandType enum ECommandType
{ {
COMMAND_OPEN_DEVICE = 1, COMMAND_OPEN_DEVICE = 1,

View file

@ -77,17 +77,15 @@ protected:
bool m_Hardware; bool m_Hardware;
bool m_Active; bool m_Active;
// =================================================== // A struct for IOS ioctlv calls
/* A struct for IOS ioctlv calls */
// ----------------
struct SIOCtlVBuffer struct SIOCtlVBuffer
{ {
SIOCtlVBuffer(u32 _Address) SIOCtlVBuffer(u32 _Address)
: m_Address(_Address) : m_Address(_Address)
{ {
/* These are the Ioctlv parameters in the IOS communication. The BufferVector // These are the Ioctlv parameters in the IOS communication. The BufferVector
is a memory address offset at where the in and out buffer addresses are // is a memory address offset at where the in and out buffer addresses are
stored. */ // stored.
Parameter = Memory::Read_U32(m_Address + 0x0C); // command 3, arg0 Parameter = Memory::Read_U32(m_Address + 0x0C); // command 3, arg0
NumberInBuffer = Memory::Read_U32(m_Address + 0x10); // 4, arg1 NumberInBuffer = Memory::Read_U32(m_Address + 0x10); // 4, arg1
NumberPayloadBuffer = Memory::Read_U32(m_Address + 0x14); // 5, arg2 NumberPayloadBuffer = Memory::Read_U32(m_Address + 0x14); // 5, arg2
@ -144,11 +142,8 @@ protected:
std::vector<SBuffer> PayloadBuffer; std::vector<SBuffer> PayloadBuffer;
}; };
// Write out the IPC struct from _CommandAddress to _NumberOfCommands numbers
// =================================================== // of 4 byte commands.
/* Write out the IPC struct from _CommandAddress to _NumberOfCommands numbers
of 4 byte commands. */
// ----------------
void DumpCommands(u32 _CommandAddress, size_t _NumberOfCommands = 8, void DumpCommands(u32 _CommandAddress, size_t _NumberOfCommands = 8,
LogTypes::LOG_TYPE LogType = LogTypes::WII_IPC_HLE, LogTypes::LOG_TYPE LogType = LogTypes::WII_IPC_HLE,
LogTypes::LOG_LEVELS Verbosity = LogTypes::LDEBUG) LogTypes::LOG_LEVELS Verbosity = LogTypes::LDEBUG)
@ -162,7 +157,6 @@ protected:
} }
} }
void DumpAsync(u32 BufferVector, u32 NumberInBuffer, u32 NumberOutBuffer, void DumpAsync(u32 BufferVector, u32 NumberInBuffer, u32 NumberOutBuffer,
LogTypes::LOG_TYPE LogType = LogTypes::WII_IPC_HLE, LogTypes::LOG_TYPE LogType = LogTypes::WII_IPC_HLE,
LogTypes::LOG_LEVELS Verbosity = LogTypes::LDEBUG) LogTypes::LOG_LEVELS Verbosity = LogTypes::LDEBUG)
@ -175,7 +169,8 @@ protected:
u32 InBuffer = Memory::Read_U32(BufferOffset); BufferOffset += 4; u32 InBuffer = Memory::Read_U32(BufferOffset); BufferOffset += 4;
u32 InBufferSize = Memory::Read_U32(BufferOffset); BufferOffset += 4; u32 InBufferSize = Memory::Read_U32(BufferOffset); BufferOffset += 4;
GENERIC_LOG(LogType, LogTypes::LINFO, "%s - IOCtlV InBuffer[%i]:", GetDeviceName().c_str(), i); GENERIC_LOG(LogType, LogTypes::LINFO, "%s - IOCtlV InBuffer[%i]:",
GetDeviceName().c_str(), i);
std::string Temp; std::string Temp;
for (u32 j = 0; j < InBufferSize; j++) for (u32 j = 0; j < InBufferSize; j++)
@ -188,22 +183,21 @@ protected:
GENERIC_LOG(LogType, LogTypes::LDEBUG, " Buffer: %s", Temp.c_str()); GENERIC_LOG(LogType, LogTypes::LDEBUG, " Buffer: %s", Temp.c_str());
} }
for (u32 i = 0; i < NumberOutBuffer; i++) for (u32 i = 0; i < NumberOutBuffer; i++)
{ {
u32 OutBuffer = Memory::Read_U32(BufferOffset); BufferOffset += 4; u32 OutBuffer = Memory::Read_U32(BufferOffset); BufferOffset += 4;
u32 OutBufferSize = Memory::Read_U32(BufferOffset); BufferOffset += 4; u32 OutBufferSize = Memory::Read_U32(BufferOffset); BufferOffset += 4;
GENERIC_LOG(LogType, LogTypes::LINFO, "%s - IOCtlV OutBuffer[%i]:", GetDeviceName().c_str(), i); GENERIC_LOG(LogType, LogTypes::LINFO, "%s - IOCtlV OutBuffer[%i]:",
GENERIC_LOG(LogType, LogTypes::LINFO, " OutBuffer: 0x%08x (0x%x):", OutBuffer, OutBufferSize); GetDeviceName().c_str(), i);
GENERIC_LOG(LogType, LogTypes::LINFO, " OutBuffer: 0x%08x (0x%x):",
OutBuffer, OutBufferSize);
#if defined(MAX_LOGLEVEL) && MAX_LOGLEVEL >= INFO_LEVEL #if defined(MAX_LOGLEVEL) && MAX_LOGLEVEL >= INFO_LEVEL
DumpCommands(OutBuffer, OutBufferSize, LogType, Verbosity); DumpCommands(OutBuffer, OutBufferSize, LogType, Verbosity);
#endif #endif
} }
} }
}; };
#endif #endif

View file

@ -37,10 +37,7 @@ enum
IOCTL_STM_READDDRREG2 = 0x4002, IOCTL_STM_READDDRREG2 = 0x4002,
}; };
// The /dev/stm/immediate
// =======================================================
// The /device/stm/immediate class
// -------------
class CWII_IPC_HLE_Device_stm_immediate : public IWII_IPC_HLE_Device class CWII_IPC_HLE_Device_stm_immediate : public IWII_IPC_HLE_Device
{ {
public: public:
@ -71,11 +68,11 @@ public:
virtual bool IOCtl(u32 _CommandAddress) virtual bool IOCtl(u32 _CommandAddress)
{ {
u32 Parameter = Memory::Read_U32(_CommandAddress +0x0C); u32 Parameter = Memory::Read_U32(_CommandAddress + 0x0C);
u32 BufferIn = Memory::Read_U32(_CommandAddress +0x10); u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10);
u32 BufferInSize = Memory::Read_U32(_CommandAddress +0x14); u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14);
u32 BufferOut = Memory::Read_U32(_CommandAddress +0x18); u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18);
u32 BufferOutSize = Memory::Read_U32(_CommandAddress +0x1C); u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C);
// Prepare the out buffer(s) with zeroes as a safety precaution // Prepare the out buffer(s) with zeroes as a safety precaution
// to avoid returning bad values // to avoid returning bad values
@ -123,16 +120,11 @@ public:
// Write return value to the IPC call // Write return value to the IPC call
Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); Memory::Write_U32(ReturnValue, _CommandAddress + 0x4);
// Generate true or false reply for the main UpdateInterrupts() function
return true; return true;
} }
}; };
// The /dev/stm/eventhook
// =======================================================
// The /device/stm/eventhook class
// -------------
class CWII_IPC_HLE_Device_stm_eventhook : public IWII_IPC_HLE_Device class CWII_IPC_HLE_Device_stm_eventhook : public IWII_IPC_HLE_Device
{ {
public: public:
@ -165,13 +157,13 @@ public:
virtual bool IOCtl(u32 _CommandAddress) virtual bool IOCtl(u32 _CommandAddress)
{ {
u32 Parameter = Memory::Read_U32(_CommandAddress +0x0C); u32 Parameter = Memory::Read_U32(_CommandAddress + 0x0C);
u32 BufferIn = Memory::Read_U32(_CommandAddress +0x10); u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10);
u32 BufferInSize = Memory::Read_U32(_CommandAddress +0x14); u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14);
u32 BufferOut = Memory::Read_U32(_CommandAddress +0x18); u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18);
u32 BufferOutSize = Memory::Read_U32(_CommandAddress +0x1C); u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C);
// Prepare the out buffer(s) with zeroes as a safety precaution // Prepare the out buffer(s) with zeros as a safety precaution
// to avoid returning bad values // to avoid returning bad values
Memory::Memset(BufferOut, 0, BufferOutSize); Memory::Memset(BufferOut, 0, BufferOutSize);
u32 ReturnValue = 0; u32 ReturnValue = 0;
@ -184,42 +176,29 @@ public:
m_EventHookAddress = _CommandAddress; m_EventHookAddress = _CommandAddress;
INFO_LOG(WII_IPC_STM, "%s registers event hook:", GetDeviceName().c_str()); INFO_LOG(WII_IPC_STM, "%s registers event hook:", GetDeviceName().c_str());
DEBUG_LOG(WII_IPC_STM, " 0x1000 - IOCTL_STM_EVENTHOOK", Parameter); DEBUG_LOG(WII_IPC_STM, "0x1000 - IOCTL_STM_EVENTHOOK", Parameter);
DEBUG_LOG(WII_IPC_STM, " BufferIn: 0x%08x", BufferIn); DEBUG_LOG(WII_IPC_STM, "BufferIn: 0x%08x", BufferIn);
DEBUG_LOG(WII_IPC_STM, " BufferInSize: 0x%08x", BufferInSize); DEBUG_LOG(WII_IPC_STM, "BufferInSize: 0x%08x", BufferInSize);
DEBUG_LOG(WII_IPC_STM, " BufferOut: 0x%08x", BufferOut); DEBUG_LOG(WII_IPC_STM, "BufferOut: 0x%08x", BufferOut);
DEBUG_LOG(WII_IPC_STM, " BufferOutSize: 0x%08x", BufferOutSize); DEBUG_LOG(WII_IPC_STM, "BufferOutSize: 0x%08x", BufferOutSize);
DumpCommands(BufferIn, BufferInSize/4, LogTypes::WII_IPC_STM); DumpCommands(BufferIn, BufferInSize/4, LogTypes::WII_IPC_STM);
return false;
} }
break; break;
default: default:
{ _dbg_assert_msg_(WII_IPC_STM, 0, "unknown %s ioctl %x",
_dbg_assert_msg_(WII_IPC_STM, 0, "CWII_IPC_HLE_Device_stm_eventhook: 0x%x", Parameter); GetDeviceName().c_str(), Parameter);
INFO_LOG(WII_IPC_STM, "%s registers event hook:", GetDeviceName().c_str());
DEBUG_LOG(WII_IPC_STM, " Parameter: 0x%x", Parameter);
DEBUG_LOG(WII_IPC_STM, " BufferIn: 0x%08x", BufferIn);
DEBUG_LOG(WII_IPC_STM, " BufferInSize: 0x%08x", BufferInSize);
DEBUG_LOG(WII_IPC_STM, " BufferOut: 0x%08x", BufferOut);
DEBUG_LOG(WII_IPC_STM, " BufferOutSize: 0x%08x", BufferOutSize);
}
break; break;
} }
// Write return value to the IPC call, 0 means success // Write return value to the IPC call, 0 means success
Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); Memory::Write_U32(ReturnValue, _CommandAddress + 0x4);
return true;
// Generate true or false reply for the main UpdateInterrupts() function
return false;
} }
// STATE_TO_SAVE // STATE_TO_SAVE
u32 m_EventHookAddress; u32 m_EventHookAddress;
}; };
#endif #endif

View file

@ -316,16 +316,11 @@ bool CWII_IPC_HLE_Device_usb_oh1_57e_305::IOCtlV(u32 _CommandAddress)
// write return value // write return value
Memory::Write_U32(0, _CommandAddress + 0x4); Memory::Write_U32(0, _CommandAddress + 0x4);
return (_SendReply); return _SendReply;
} }
// ================
// Here we handle the USB_IOCTL_BLKMSG Ioctlv
// ===================================================
/* Here we handle the USB_IOCTL_BLKMSG Ioctlv */
// ----------------
void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendToDevice(u16 _ConnectionHandle, u8* _pData, u32 _Size) void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendToDevice(u16 _ConnectionHandle, u8* _pData, u32 _Size)
{ {
CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_ConnectionHandle); CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_ConnectionHandle);
@ -337,18 +332,12 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendToDevice(u16 _ConnectionHandle, u8
pWiiMote->ExecuteL2capCmd(_pData, _Size); pWiiMote->ExecuteL2capCmd(_pData, _Size);
} }
// ================
// ===================================================
// Here we send ACL pakcets to CPU. They will consist of header + data. // Here we send ACL pakcets to CPU. They will consist of header + data.
// The header is for example 07 00 41 00 which means size 0x0007 and channel 0x0041. // The header is for example 07 00 41 00 which means size 0x0007 and channel 0x0041.
// --------------------------------------------------- // ---------------------------------------------------
// AyuanX: Basically, our WII_IPC_HLE is efficient enough to send the packet immediately // AyuanX: Basically, our WII_IPC_HLE is efficient enough to send the packet immediately
// rather than enqueue it to some other memory // rather than enqueue it to some other memory
// But...the only exception comes from the Wiimote_Plugin // But...the only exception comes from the Wiimote_Plugin
//
void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendACLPacket(u16 _ConnectionHandle, u8* _pData, u32 _Size) void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendACLPacket(u16 _ConnectionHandle, u8* _pData, u32 _Size)
{ {
if(m_ACLBuffer.m_address != 0) if(m_ACLBuffer.m_address != 0)
@ -368,7 +357,7 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendACLPacket(u16 _ConnectionHandle, u
Memory::Write_U32(sizeof(UACLHeader) + _Size, m_ACLBuffer.m_address + 0x4); Memory::Write_U32(sizeof(UACLHeader) + _Size, m_ACLBuffer.m_address + 0x4);
// Send a reply to indicate ACL buffer is sent // Send a reply to indicate ACL buffer is sent
WII_IPCInterface::EnqReply(m_ACLBuffer.m_address); WII_IPC_HLE_Interface::EnqReply(m_ACLBuffer.m_address);
// Invalidate ACL buffer // Invalidate ACL buffer
m_ACLBuffer.m_address = 0; m_ACLBuffer.m_address = 0;
@ -403,7 +392,6 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendACLPacket(u16 _ConnectionHandle, u
// but current implementation of WiiMote_Plugin doesn't comply with this rule // but current implementation of WiiMote_Plugin doesn't comply with this rule
// It acts like sending all the 3 packets in one cycle and idling around in the other two cycles // It acts like sending all the 3 packets in one cycle and idling around in the other two cycles
// that's why we need this contingent ACL pool // that's why we need this contingent ACL pool
//
void CWII_IPC_HLE_Device_usb_oh1_57e_305::PurgeACLPool() void CWII_IPC_HLE_Device_usb_oh1_57e_305::PurgeACLPool()
{ {
if(m_ACLBuffer.m_address == 0) if(m_ACLBuffer.m_address == 0)
@ -420,16 +408,14 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::PurgeACLPool()
// Write the packet size as return value // Write the packet size as return value
Memory::Write_U32(sizeof(UACLHeader) + ((UACLHeader*)_Address)->Size, m_ACLBuffer.m_address + 0x4); Memory::Write_U32(sizeof(UACLHeader) + ((UACLHeader*)_Address)->Size, m_ACLBuffer.m_address + 0x4);
// Send a reply to indicate ACL buffer is sent // Send a reply to indicate ACL buffer is sent
WII_IPCInterface::EnqReply(m_ACLBuffer.m_address); WII_IPC_HLE_Interface::EnqReply(m_ACLBuffer.m_address);
// Invalidate ACL buffer // Invalidate ACL buffer
m_ACLBuffer.m_address = 0; m_ACLBuffer.m_address = 0;
m_ACLBuffer.m_buffer = NULL; m_ACLBuffer.m_buffer = NULL;
} }
} }
// =================================================== // See IPC_HLE_PERIOD in SystemTimers.cpp for a documentation of this update.
/* See IPC_HLE_PERIOD in SystemTimers.cpp for a documentation of this update. */
// ----------------
u32 CWII_IPC_HLE_Device_usb_oh1_57e_305::Update() u32 CWII_IPC_HLE_Device_usb_oh1_57e_305::Update()
{ {
// Check if HCI Pool is not purged // Check if HCI Pool is not purged
@ -437,7 +423,7 @@ u32 CWII_IPC_HLE_Device_usb_oh1_57e_305::Update()
{ {
PurgeHCIPool(); PurgeHCIPool();
if (m_HCIPool.m_number == 0) if (m_HCIPool.m_number == 0)
WII_IPCInterface::EnqReply(m_CtrlSetup.m_Address); WII_IPC_HLE_Interface::EnqReply(m_CtrlSetup.m_Address);
return true; return true;
} }
@ -453,23 +439,22 @@ u32 CWII_IPC_HLE_Device_usb_oh1_57e_305::Update()
{ {
PurgeACLPool(); PurgeACLPool();
if (m_ACLPool.m_number == 0) if (m_ACLPool.m_number == 0)
WII_IPCInterface::EnqReply(m_ACLSetup); WII_IPC_HLE_Interface::EnqReply(m_ACLSetup);
return true; return true;
} }
// --------------------------------------------------------------------
/* We wait for ScanEnable to be sent from the game through HCI_CMD_WRITE_SCAN_ENABLE /* We wait for ScanEnable to be sent from the game through HCI_CMD_WRITE_SCAN_ENABLE
before we initiate the connection. before we initiate the connection.
FiRES: TODO find a good solution to do this FiRES: TODO find a good solution to do this
/* Why do we need this? 0 worked with the emulated wiimote in all games I tried. Do we have to Why do we need this? 0 worked with the emulated wiimote in all games I tried. Do we have to
wait for wiiuse_init() and wiiuse_find() for a real Wiimote here? I'm testing wait for wiiuse_init() and wiiuse_find() for a real Wiimote here? I'm testing
this new method of not waiting at all if there are no real Wiimotes. Please let me know this new method of not waiting at all if there are no real Wiimotes. Please let me know
if it doesn't work. */ if it doesn't work. */
// AyuanX: I don't know the Real Wiimote behavior, so I'll leave it here untouched // AyuanX: I don't know the Real Wiimote behavior, so I'll leave it here untouched
//
// Initiate ACL connection // Initiate ACL connection
static int counter = (Core::GetRealWiimote() ? 1000 : 0); static int counter = (Core::GetRealWiimote() ? 1000 : 0);
if (m_HCIBuffer.m_address && (m_ScanEnable & 0x2)) if (m_HCIBuffer.m_address && (m_ScanEnable & 0x2))
@ -537,17 +522,13 @@ u32 CWII_IPC_HLE_Device_usb_oh1_57e_305::Update()
return false; return false;
} }
// Events // Events
// ----------------- // -----------------
// These messages are sent from the Wiimote to the game, for example RequestConnection() // These messages are sent from the Wiimote to the game, for example RequestConnection()
// or ConnectionComplete(). // or ConnectionComplete().
// //
// Our WII_IPC_HLE is so efficient that we could fill the buffer immediately // Our WII_IPC_HLE is so efficient that we could fill the buffer immediately
// rather than enqueue it to some other memory and this will do good for StateSave // rather than enqueue it to some other memory and this will do good for StateSave
//
void CWII_IPC_HLE_Device_usb_oh1_57e_305::AddEventToQueue(const SQueuedEvent& _event) void CWII_IPC_HLE_Device_usb_oh1_57e_305::AddEventToQueue(const SQueuedEvent& _event)
{ {
if (m_HCIBuffer.m_address != 0) if (m_HCIBuffer.m_address != 0)
@ -560,7 +541,7 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::AddEventToQueue(const SQueuedEvent& _e
Memory::Write_U32((u32)_event.m_size, m_HCIBuffer.m_address + 0x4); Memory::Write_U32((u32)_event.m_size, m_HCIBuffer.m_address + 0x4);
// Send a reply to indicate HCI buffer is filled // Send a reply to indicate HCI buffer is filled
WII_IPCInterface::EnqReply(m_HCIBuffer.m_address); WII_IPC_HLE_Interface::EnqReply(m_HCIBuffer.m_address);
// Invalidate HCI buffer // Invalidate HCI buffer
m_HCIBuffer.m_address = 0; m_HCIBuffer.m_address = 0;
@ -590,7 +571,6 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::AddEventToQueue(const SQueuedEvent& _e
// so when IPC is running too fast that CPU can't catch up // so when IPC is running too fast that CPU can't catch up
// then CPU(actually it is the usb driver) sometimes throws out a command before sending us a HCI buffer // then CPU(actually it is the usb driver) sometimes throws out a command before sending us a HCI buffer
// So I put this contingent HCI Pool here until we figure out the true reason // So I put this contingent HCI Pool here until we figure out the true reason
//
void CWII_IPC_HLE_Device_usb_oh1_57e_305::PurgeHCIPool() void CWII_IPC_HLE_Device_usb_oh1_57e_305::PurgeHCIPool()
{ {
if(m_HCIBuffer.m_address == 0) if(m_HCIBuffer.m_address == 0)
@ -607,7 +587,7 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::PurgeHCIPool()
// Write the packet size as return value // Write the packet size as return value
Memory::Write_U32(m_HCIPool.m_size[m_HCIPool.m_number], m_HCIBuffer.m_address + 0x4); Memory::Write_U32(m_HCIPool.m_size[m_HCIPool.m_number], m_HCIBuffer.m_address + 0x4);
// Send a reply to indicate ACL buffer is sent // Send a reply to indicate ACL buffer is sent
WII_IPCInterface::EnqReply(m_HCIBuffer.m_address); WII_IPC_HLE_Interface::EnqReply(m_HCIBuffer.m_address);
// Invalidate ACL buffer // Invalidate ACL buffer
m_HCIBuffer.m_address = 0; m_HCIBuffer.m_address = 0;
m_HCIBuffer.m_buffer = NULL; m_HCIBuffer.m_buffer = NULL;
@ -714,7 +694,7 @@ bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventConnectionComplete(const bdad
return true; return true;
} }
/* This is called from Update() after ScanEnable has been enabled. */ // This is called from Update() after ScanEnable has been enabled.
bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventRequestConnection(CWII_IPC_HLE_WiiMote& _rWiiMote) bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventRequestConnection(CWII_IPC_HLE_WiiMote& _rWiiMote)
{ {
SQueuedEvent Event(sizeof(SHCIEventRequestConnection), 0); SQueuedEvent Event(sizeof(SHCIEventRequestConnection), 0);
@ -1137,8 +1117,6 @@ bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventConPacketTypeChange(u16 _conn
// Command dispatcher // Command dispatcher
// ----------------- // -----------------
// This is called from the USB_IOCTL_HCI_COMMAND_MESSAGE Ioctlv // This is called from the USB_IOCTL_HCI_COMMAND_MESSAGE Ioctlv
//
//
void CWII_IPC_HLE_Device_usb_oh1_57e_305::ExecuteHCICommandMessage(const SHCICommandMessage& _rHCICommandMessage) void CWII_IPC_HLE_Device_usb_oh1_57e_305::ExecuteHCICommandMessage(const SHCICommandMessage& _rHCICommandMessage)
{ {
u8* pInput = Memory::GetPointer(_rHCICommandMessage.m_PayLoadAddr + 3); u8* pInput = Memory::GetPointer(_rHCICommandMessage.m_PayLoadAddr + 3);
@ -1329,7 +1307,7 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::ExecuteHCICommandMessage(const SHCICom
if ((m_LastCmd == 0) && (m_HCIPool.m_number == 0)) if ((m_LastCmd == 0) && (m_HCIPool.m_number == 0))
{ {
// If HCI command is finished and HCI pool is empty, send a reply to command // If HCI command is finished and HCI pool is empty, send a reply to command
WII_IPCInterface::EnqReply(_rHCICommandMessage.m_Address); WII_IPC_HLE_Interface::EnqReply(_rHCICommandMessage.m_Address);
} }
} }