IPCCommandResult STMImmediate::IOCtl(const IOCtlRequest& request) { s32 return_value = IPC_SUCCESS; switch (request.request) { case IOCTL_STM_IDLE: case IOCTL_STM_SHUTDOWN: NOTICE_LOG(IOS_STM, "IOCTL_STM_IDLE or IOCTL_STM_SHUTDOWN received, shutting down"); Core::QueueHostJob(&Core::Stop, false); break; case IOCTL_STM_RELEASE_EH: if (!s_event_hook_request) { return_value = IPC_ENOENT; break; } Memory::Write_U32(0, s_event_hook_request->buffer_out); EnqueueReply(*s_event_hook_request, IPC_SUCCESS); s_event_hook_request.reset(); break; case IOCTL_STM_HOTRESET: INFO_LOG(IOS_STM, "%s - IOCtl:", GetDeviceName().c_str()); INFO_LOG(IOS_STM, " IOCTL_STM_HOTRESET"); break; case IOCTL_STM_VIDIMMING: // (Input: 20 bytes, Output: 20 bytes) INFO_LOG(IOS_STM, "%s - IOCtl:", GetDeviceName().c_str()); INFO_LOG(IOS_STM, " IOCTL_STM_VIDIMMING"); // Memory::Write_U32(1, buffer_out); // return_value = 1; break; case IOCTL_STM_LEDMODE: // (Input: 20 bytes, Output: 20 bytes) INFO_LOG(IOS_STM, "%s - IOCtl:", GetDeviceName().c_str()); INFO_LOG(IOS_STM, " IOCTL_STM_LEDMODE"); break; default: request.DumpUnknown(GetDeviceName(), LogTypes::IOS_STM); } return GetDefaultReply(return_value); }
void ExecuteCommand(u32 _Address) { bool CmdSuccess = false; IPCCommandType Command = static_cast<IPCCommandType>(Memory::Read_U32(_Address)); volatile s32 DeviceID = Memory::Read_U32(_Address + 8); IWII_IPC_HLE_Device* pDevice = (DeviceID >= 0 && DeviceID < IPC_MAX_FDS) ? g_FdMap[DeviceID] : nullptr; INFO_LOG(WII_IPC_HLE, "-->> Execute Command Address: 0x%08x (code: %x, device: %x) %p", _Address, Command, DeviceID, pDevice); switch (Command) { case IPC_CMD_OPEN: { u32 Mode = Memory::Read_U32(_Address + 0x10); DeviceID = getFreeDeviceId(); std::string DeviceName = Memory::GetString(Memory::Read_U32(_Address + 0xC)); WARN_LOG(WII_IPC_HLE, "Trying to open %s as %d", DeviceName.c_str(), DeviceID); if (DeviceID >= 0) { if (DeviceName.find("/dev/es") == 0) { u32 j; for (j=0; j<ES_MAX_COUNT; j++) { if (!es_inuse[j]) { es_inuse[j] = true; g_FdMap[DeviceID] = es_handles[j]; CmdSuccess = es_handles[j]->Open(_Address, Mode); Memory::Write_U32(DeviceID, _Address+4); break; } } if (j == ES_MAX_COUNT) { Memory::Write_U32(FS_EESEXHAUSTED, _Address + 4); CmdSuccess = true; } } else if (DeviceName.find("/dev/") == 0) { pDevice = GetDeviceByName(DeviceName); if (pDevice) { g_FdMap[DeviceID] = pDevice; CmdSuccess = pDevice->Open(_Address, Mode); INFO_LOG(WII_IPC_FILEIO, "IOP: ReOpen (Device=%s, DeviceID=%08x, Mode=%i)", pDevice->GetDeviceName().c_str(), DeviceID, Mode); Memory::Write_U32(DeviceID, _Address+4); } else { WARN_LOG(WII_IPC_HLE, "Unimplemented device: %s", DeviceName.c_str()); Memory::Write_U32(FS_ENOENT, _Address+4); CmdSuccess = true; } } else { pDevice = CreateFileIO(DeviceID, DeviceName); CmdSuccess = pDevice->Open(_Address, Mode); INFO_LOG(WII_IPC_FILEIO, "IOP: Open File (Device=%s, ID=%08x, Mode=%i)", pDevice->GetDeviceName().c_str(), DeviceID, Mode); if (Memory::Read_U32(_Address + 4) == (u32)DeviceID) { g_FdMap[DeviceID] = pDevice; } else { delete pDevice; pDevice = nullptr; } } } else { Memory::Write_U32(FS_EFDEXHAUSTED, _Address + 4); CmdSuccess = true; } break; } case IPC_CMD_CLOSE: { if (pDevice) { CmdSuccess = pDevice->Close(_Address); for (u32 j=0; j<ES_MAX_COUNT; j++) { if (es_handles[j] == g_FdMap[DeviceID]) { es_inuse[j] = false; } } g_FdMap[DeviceID] = nullptr; // Don't delete hardware if (!pDevice->IsHardware()) { delete pDevice; pDevice = nullptr; } } else { Memory::Write_U32(FS_EINVAL, _Address + 4); CmdSuccess = true; } break; } case IPC_CMD_READ: { if (pDevice) { CmdSuccess = pDevice->Read(_Address); } else { Memory::Write_U32(FS_EINVAL, _Address + 4); CmdSuccess = true; } break; } case IPC_CMD_WRITE: { if (pDevice) { CmdSuccess = pDevice->Write(_Address); } else { Memory::Write_U32(FS_EINVAL, _Address + 4); CmdSuccess = true; } break; } case IPC_CMD_SEEK: { if (pDevice) { CmdSuccess = pDevice->Seek(_Address); } else { Memory::Write_U32(FS_EINVAL, _Address + 4); CmdSuccess = true; } break; } case IPC_CMD_IOCTL: { if (pDevice) { CmdSuccess = pDevice->IOCtl(_Address); } break; } case IPC_CMD_IOCTLV: { if (pDevice) { CmdSuccess = pDevice->IOCtlV(_Address); } break; } default: { _dbg_assert_msg_(WII_IPC_HLE, 0, "Unknown IPC Command %i (0x%08x)", Command, _Address); break; } } if (CmdSuccess) { // The original hardware overwrites the command type with the async reply type. Memory::Write_U32(IPC_REP_ASYNC, _Address); // IOS also seems to write back the command that was responded to in the FD field. Memory::Write_U32(Command, _Address + 8); // Ensure replies happen in order, fairly ugly // Without this, tons of games fail now that DI commands have different reply delays int reply_delay = pDevice ? pDevice->GetCmdDelay(_Address) : 0; if (!reply_delay) { int delay_us = 250; reply_delay = SystemTimers::GetTicksPerSecond() / 1000000 * delay_us; } const s64 ticks_til_last_reply = last_reply_time - CoreTiming::GetTicks(); if (ticks_til_last_reply > 0) { reply_delay = (int)ticks_til_last_reply; } last_reply_time = CoreTiming::GetTicks() + reply_delay; // Generate a reply to the IPC command EnqueueReply(_Address, reply_delay); } }
// The front SD slot IPCCommandResult CWII_IPC_HLE_Device_sdio_slot0::IOCtl(u32 _CommandAddress) { u32 Cmd = Memory::Read_U32(_CommandAddress + 0xC); 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); // As a safety precaution we fill the out buffer with zeros to avoid // returning nonsense values Memory::Memset(BufferOut, 0, BufferOutSize); u32 ReturnValue = 0; switch (Cmd) { case IOCTL_WRITEHCR: { u32 reg = Memory::Read_U32(BufferIn); u32 val = Memory::Read_U32(BufferIn + 16); DEBUG_LOG(WII_IPC_SD, "IOCTL_WRITEHCR 0x%08x - 0x%08x", reg, val); if (reg >= 0x200) { DEBUG_LOG(WII_IPC_SD, "IOCTL_WRITEHCR out of range"); break; } if ((reg == HCR_CLOCKCONTROL) && (val & 1)) { // Clock is set to oscillate, enable bit 1 to say it's stable m_Registers[reg] = val | 2; } else if ((reg == HCR_SOFTWARERESET) && val) { // When a reset is specified, the register gets cleared m_Registers[reg] = 0; } else { // Default to just storing the new value m_Registers[reg] = val; } } break; case IOCTL_READHCR: { u32 reg = Memory::Read_U32(BufferIn); if (reg >= 0x200) { DEBUG_LOG(WII_IPC_SD, "IOCTL_READHCR out of range"); break; } u32 val = m_Registers[reg]; DEBUG_LOG(WII_IPC_SD, "IOCTL_READHCR 0x%08x - 0x%08x", reg, val); // Just reading the register Memory::Write_U32(val, BufferOut); } break; case IOCTL_RESETCARD: DEBUG_LOG(WII_IPC_SD, "IOCTL_RESETCARD"); if (m_Card) m_Status |= CARD_INITIALIZED; // Returns 16bit RCA and 16bit 0s (meaning success) Memory::Write_U32(0x9f620000, BufferOut); break; case IOCTL_SETCLK: { DEBUG_LOG(WII_IPC_SD, "IOCTL_SETCLK"); // libogc only sets it to 1 and makes sure the return isn't negative... // one half of the sdclk divisor: a power of two or zero. u32 clock = Memory::Read_U32(BufferIn); if (clock != 1) INFO_LOG(WII_IPC_SD, "Setting to %i, interesting", clock); } break; case IOCTL_SENDCMD: INFO_LOG(WII_IPC_SD, "IOCTL_SENDCMD %x IPC:%08x", Memory::Read_U32(BufferIn), _CommandAddress); ReturnValue = ExecuteCommand(BufferIn, BufferInSize, 0, 0, BufferOut, BufferOutSize); break; case IOCTL_GETSTATUS: if (SConfig::GetInstance().m_WiiSDCard) m_Status |= CARD_INSERTED; else m_Status = CARD_NOT_EXIST; INFO_LOG(WII_IPC_SD, "IOCTL_GETSTATUS. Replying that SD card is %s%s", (m_Status & CARD_INSERTED) ? "inserted" : "not present", (m_Status & CARD_INITIALIZED) ? " and initialized" : ""); Memory::Write_U32(m_Status, BufferOut); break; case IOCTL_GETOCR: DEBUG_LOG(WII_IPC_SD, "IOCTL_GETOCR"); Memory::Write_U32(0x80ff8000, BufferOut); break; default: ERROR_LOG(WII_IPC_SD, "Unknown SD IOCtl command (0x%08x)", Cmd); break; } // INFO_LOG(WII_IPC_SD, "InBuffer"); // DumpCommands(BufferIn, BufferInSize / 4, LogTypes::WII_IPC_SD); // INFO_LOG(WII_IPC_SD, "OutBuffer"); // DumpCommands(BufferOut, BufferOutSize/4, LogTypes::WII_IPC_SD); if (ReturnValue == RET_EVENT_REGISTER) { // async m_event.addr = _CommandAddress; Memory::Write_U32(0, _CommandAddress + 0x4); // Check if the condition is already true EventNotify(); return IPC_NO_REPLY; } else if (ReturnValue == RET_EVENT_UNREGISTER) { // release returns 0 // unknown sd int // technically we do it out of order, oh well EnqueueReply(m_event.addr, EVENT_INVALID); m_event.addr = 0; m_event.type = EVENT_NONE; Memory::Write_U32(0, _CommandAddress + 0x4); return IPC_DEFAULT_REPLY; } else { Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); return IPC_DEFAULT_REPLY; } }
void ExecuteCommand(u32 _Address) { IPCCommandResult result = IWII_IPC_HLE_Device::GetNoReply(); IPCCommandType Command = static_cast<IPCCommandType>(Memory::Read_U32(_Address)); s32 DeviceID = Memory::Read_U32(_Address + 8); std::shared_ptr<IWII_IPC_HLE_Device> pDevice = (DeviceID >= 0 && DeviceID < IPC_MAX_FDS) ? g_FdMap[DeviceID] : nullptr; INFO_LOG(WII_IPC_HLE, "-->> Execute Command Address: 0x%08x (code: %x, device: %x) %p", _Address, Command, DeviceID, pDevice.get()); switch (Command) { case IPC_CMD_OPEN: { u32 Mode = Memory::Read_U32(_Address + 0x10); DeviceID = getFreeDeviceId(); std::string DeviceName = Memory::GetString(Memory::Read_U32(_Address + 0xC)); WARN_LOG(WII_IPC_HLE, "Trying to open %s as %d", DeviceName.c_str(), DeviceID); if (DeviceID >= 0) { if (DeviceName.find("/dev/es") == 0) { u32 j; for (j=0; j<ES_MAX_COUNT; j++) { if (!es_inuse[j]) { es_inuse[j] = true; g_FdMap[DeviceID] = es_handles[j]; result = es_handles[j]->Open(_Address, Mode); Memory::Write_U32(DeviceID, _Address+4); break; } } if (j == ES_MAX_COUNT) { Memory::Write_U32(FS_EESEXHAUSTED, _Address + 4); result = IWII_IPC_HLE_Device::GetDefaultReply(); } } else if (DeviceName.find("/dev/") == 0) { pDevice = GetDeviceByName(DeviceName); if (pDevice) { g_FdMap[DeviceID] = pDevice; result = pDevice->Open(_Address, Mode); INFO_LOG(WII_IPC_FILEIO, "IOP: ReOpen (Device=%s, DeviceID=%08x, Mode=%i)", pDevice->GetDeviceName().c_str(), DeviceID, Mode); Memory::Write_U32(DeviceID, _Address+4); } else { WARN_LOG(WII_IPC_HLE, "Unimplemented device: %s", DeviceName.c_str()); Memory::Write_U32(FS_ENOENT, _Address+4); result = IWII_IPC_HLE_Device::GetDefaultReply(); } } else { pDevice = CreateFileIO(DeviceID, DeviceName); result = pDevice->Open(_Address, Mode); INFO_LOG(WII_IPC_FILEIO, "IOP: Open File (Device=%s, ID=%08x, Mode=%i)", pDevice->GetDeviceName().c_str(), DeviceID, Mode); if (Memory::Read_U32(_Address + 4) == (u32)DeviceID) { g_FdMap[DeviceID] = pDevice; } } } else { Memory::Write_U32(FS_EFDEXHAUSTED, _Address + 4); result = IWII_IPC_HLE_Device::GetDefaultReply(); } break; } case IPC_CMD_CLOSE: { if (pDevice) { result = pDevice->Close(_Address); for (u32 j=0; j<ES_MAX_COUNT; j++) { if (es_handles[j] == g_FdMap[DeviceID]) { es_inuse[j] = false; } } g_FdMap[DeviceID].reset(); } else { Memory::Write_U32(FS_EINVAL, _Address + 4); result = IWII_IPC_HLE_Device::GetDefaultReply(); } break; } case IPC_CMD_READ: { if (pDevice) { result = pDevice->Read(_Address); } else { Memory::Write_U32(FS_EINVAL, _Address + 4); result = IWII_IPC_HLE_Device::GetDefaultReply(); } break; } case IPC_CMD_WRITE: { if (pDevice) { result = pDevice->Write(_Address); } else { Memory::Write_U32(FS_EINVAL, _Address + 4); result = IWII_IPC_HLE_Device::GetDefaultReply(); } break; } case IPC_CMD_SEEK: { if (pDevice) { result = pDevice->Seek(_Address); } else { Memory::Write_U32(FS_EINVAL, _Address + 4); result = IWII_IPC_HLE_Device::GetDefaultReply(); } break; } case IPC_CMD_IOCTL: { if (pDevice) { result = pDevice->IOCtl(_Address); } break; } case IPC_CMD_IOCTLV: { if (pDevice) { result = pDevice->IOCtlV(_Address); } break; } default: { _dbg_assert_msg_(WII_IPC_HLE, 0, "Unknown IPC Command %i (0x%08x)", Command, _Address); break; } } // Ensure replies happen in order const s64 ticks_until_last_reply = last_reply_time - CoreTiming::GetTicks(); if (ticks_until_last_reply > 0) result.reply_delay_ticks += ticks_until_last_reply; last_reply_time = CoreTiming::GetTicks() + result.reply_delay_ticks; if (result.send_reply) { // The original hardware overwrites the command type with the async reply type. Memory::Write_U32(IPC_REP_ASYNC, _Address); // IOS also seems to write back the command that was responded to in the FD field. Memory::Write_U32(Command, _Address + 8); // Generate a reply to the IPC command EnqueueReply(_Address, (int)result.reply_delay_ticks); } }