Merge pull request #4244 from leoetlino/stm-shutdown

Shut down Wii software gracefully
This commit is contained in:
shuffle2 2016-10-02 20:25:13 -07:00 committed by GitHub
commit 5e8bc4aa1d
12 changed files with 268 additions and 146 deletions

View File

@ -314,7 +314,7 @@ void SysConf::GenerateSysConf()
// IPL.IDL
current_offset += create_item(items[23], Type_SmallArray, "IPL.IDL", 1, current_offset);
items[23].data[0] = 0x01;
items[23].data[0] = 0x00;
// IPL.EULA
current_offset += create_item(items[24], Type_Bool, "IPL.EULA", 1, current_offset);

View File

@ -392,6 +392,15 @@ bool BootCore(const std::string& _rFilename)
SConfig::GetInstance().m_SYSCONF->SetData("IPL.PGS", StartUp.bProgressive);
SConfig::GetInstance().m_SYSCONF->SetData("IPL.E60", StartUp.bPAL60);
if (StartUp.bWii)
{
// Disable WiiConnect24's standby mode. If it is enabled, it prevents us from receiving
// shutdown commands in the State Transition Manager (STM).
// TODO: remove this if and once Dolphin supports WC24 standby mode.
SConfig::GetInstance().m_SYSCONF->SetData("IPL.IDL", 0x00);
NOTICE_LOG(BOOT, "Disabling WC24 'standby' (shutdown to idle) to avoid hanging on shutdown");
}
// Run the game
// Init the core
if (!Core::Init())

View File

@ -143,6 +143,7 @@ set(SRCS ActionReplay.cpp
IPC_HLE/WII_Socket.cpp
IPC_HLE/WII_IPC_HLE_Device_net.cpp
IPC_HLE/WII_IPC_HLE_Device_net_ssl.cpp
IPC_HLE/WII_IPC_HLE_Device_stm.cpp
IPC_HLE/WII_IPC_HLE_Device_sdio_slot0.cpp
IPC_HLE/WII_IPC_HLE_Device_usb.cpp
IPC_HLE/WII_IPC_HLE_Device_usb_kbd.cpp

View File

@ -183,6 +183,7 @@
</ClCompile>
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_net.cpp" />
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_net_ssl.cpp" />
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_stm.cpp" />
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_sdio_slot0.cpp" />
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_usb.cpp" />
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_usb_kbd.cpp" />
@ -386,8 +387,8 @@
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_hid.h" />
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_net.h" />
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_net_ssl.h" />
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_sdio_slot0.h" />
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_stm.h" />
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_sdio_slot0.h" />
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_usb.h" />
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_usb_kbd.h" />
<ClInclude Include="IPC_HLE\WII_IPC_HLE_Device_usb_ven.h" />

View File

@ -585,6 +585,9 @@
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_sdio_slot0.cpp">
<Filter>IPC HLE %28IOS/Starlet%29\SDIO - SD Card</Filter>
</ClCompile>
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_stm.cpp">
<Filter>IPC HLE %28IOS/Starlet%29</Filter>
</ClCompile>
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_hid.cpp">
<Filter>IPC HLE %28IOS/Starlet%29\USB</Filter>
</ClCompile>

View File

@ -37,6 +37,9 @@ static void ToggleResetButtonCallback(u64 userdata, s64 cyclesLate);
static CoreTiming::EventType* iosNotifyResetButton;
static void IOSNotifyResetButtonCallback(u64 userdata, s64 cyclesLate);
static CoreTiming::EventType* iosNotifyPowerButton;
static void IOSNotifyPowerButtonCallback(u64 userdata, s64 cyclesLate);
// Let the PPC know that an external exception is set/cleared
void UpdateException();
@ -75,6 +78,8 @@ void Init()
toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback);
iosNotifyResetButton =
CoreTiming::RegisterEvent("IOSNotifyResetButton", IOSNotifyResetButtonCallback);
iosNotifyPowerButton =
CoreTiming::RegisterEvent("IOSNotifyPowerButton", IOSNotifyPowerButtonCallback);
}
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
@ -214,6 +219,17 @@ static void IOSNotifyResetButtonCallback(u64 userdata, s64 cyclesLate)
}
}
static void IOSNotifyPowerButtonCallback(u64 userdata, s64 cyclesLate)
{
if (SConfig::GetInstance().bWii)
{
std::shared_ptr<IWII_IPC_HLE_Device> stm =
WII_IPC_HLE_Interface::GetDeviceByName("/dev/stm/eventhook");
if (stm)
std::static_pointer_cast<CWII_IPC_HLE_Device_stm_eventhook>(stm)->PowerButton();
}
}
void ResetButton_Tap()
{
CoreTiming::ScheduleEvent(0, toggleResetButton, true, CoreTiming::FromThread::ANY);
@ -222,4 +238,9 @@ void ResetButton_Tap()
CoreTiming::FromThread::ANY);
}
void PowerButton_Tap()
{
CoreTiming::ScheduleEvent(0, iosNotifyPowerButton, 0, CoreTiming::FromThread::ANY);
}
} // namespace ProcessorInterface

View File

@ -76,5 +76,6 @@ void SetInterrupt(u32 _causemask, bool _bSet = true);
// Thread-safe func which sets and clears reset button state automagically
void ResetButton_Tap();
void PowerButton_Tap();
} // namespace ProcessorInterface

View File

@ -0,0 +1,169 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/IPC_HLE/WII_IPC_HLE_Device_stm.h"
namespace Core
{
void QueueHostJob(std::function<void()> job, bool run_during_stop);
void Stop();
}
static u32 s_event_hook_address = 0;
IPCCommandResult CWII_IPC_HLE_Device_stm_immediate::Open(u32 command_address, u32 mode)
{
INFO_LOG(WII_IPC_STM, "STM immediate: Open");
Memory::Write_U32(GetDeviceID(), command_address + 4);
m_Active = true;
return GetDefaultReply();
}
IPCCommandResult CWII_IPC_HLE_Device_stm_immediate::Close(u32 command_address, bool force)
{
INFO_LOG(WII_IPC_STM, "STM immediate: Close");
if (!force)
Memory::Write_U32(0, command_address + 4);
m_Active = false;
return GetDefaultReply();
}
IPCCommandResult CWII_IPC_HLE_Device_stm_immediate::IOCtl(u32 command_address)
{
u32 parameter = Memory::Read_U32(command_address + 0x0C);
u32 buffer_in = Memory::Read_U32(command_address + 0x10);
u32 buffer_in_size = Memory::Read_U32(command_address + 0x14);
u32 buffer_out = Memory::Read_U32(command_address + 0x18);
u32 buffer_out_size = Memory::Read_U32(command_address + 0x1C);
// Prepare the out buffer(s) with zeroes as a safety precaution
// to avoid returning bad values
Memory::Memset(buffer_out, 0, buffer_out_size);
u32 return_value = 0;
switch (parameter)
{
case IOCTL_STM_IDLE:
case IOCTL_STM_SHUTDOWN:
NOTICE_LOG(WII_IPC_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_address == 0)
{
return_value = FS_ENOENT;
break;
}
Memory::Write_U32(0, Memory::Read_U32(s_event_hook_address + 0x18));
Memory::Write_U32(FS_SUCCESS, s_event_hook_address + 4);
Memory::Write_U32(IPC_REP_ASYNC, s_event_hook_address);
Memory::Write_U32(IPC_CMD_IOCTL, s_event_hook_address + 8);
WII_IPC_HLE_Interface::EnqueueReply(s_event_hook_address);
s_event_hook_address = 0;
break;
case IOCTL_STM_HOTRESET:
INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str());
INFO_LOG(WII_IPC_STM, " IOCTL_STM_HOTRESET");
break;
case IOCTL_STM_VIDIMMING: // (Input: 20 bytes, Output: 20 bytes)
INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str());
INFO_LOG(WII_IPC_STM, " IOCTL_STM_VIDIMMING");
// DumpCommands(buffer_in, buffer_in_size / 4, LogTypes::WII_IPC_STM);
// Memory::Write_U32(1, buffer_out);
// return_value = 1;
break;
case IOCTL_STM_LEDMODE: // (Input: 20 bytes, Output: 20 bytes)
INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str());
INFO_LOG(WII_IPC_STM, " IOCTL_STM_LEDMODE");
break;
default:
{
_dbg_assert_msg_(WII_IPC_STM, 0, "CWII_IPC_HLE_Device_stm_immediate: 0x%x", parameter);
INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str());
DEBUG_LOG(WII_IPC_STM, " parameter: 0x%x", parameter);
DEBUG_LOG(WII_IPC_STM, " InBuffer: 0x%08x", buffer_in);
DEBUG_LOG(WII_IPC_STM, " InBufferSize: 0x%08x", buffer_in_size);
DEBUG_LOG(WII_IPC_STM, " OutBuffer: 0x%08x", buffer_out);
DEBUG_LOG(WII_IPC_STM, " OutBufferSize: 0x%08x", buffer_out_size);
}
break;
}
// Write return value to the IPC call
Memory::Write_U32(return_value, command_address + 0x4);
return GetDefaultReply();
}
IPCCommandResult CWII_IPC_HLE_Device_stm_eventhook::Open(u32 command_address, u32 mode)
{
Memory::Write_U32(GetDeviceID(), command_address + 4);
m_Active = true;
return GetDefaultReply();
}
IPCCommandResult CWII_IPC_HLE_Device_stm_eventhook::Close(u32 command_address, bool force)
{
s_event_hook_address = 0;
INFO_LOG(WII_IPC_STM, "STM eventhook: Close");
if (!force)
Memory::Write_U32(0, command_address + 4);
m_Active = false;
return GetDefaultReply();
}
IPCCommandResult CWII_IPC_HLE_Device_stm_eventhook::IOCtl(u32 command_address)
{
u32 parameter = Memory::Read_U32(command_address + 0x0C);
if (parameter != IOCTL_STM_EVENTHOOK)
{
ERROR_LOG(WII_IPC_STM, "Bad IOCtl in CWII_IPC_HLE_Device_stm_eventhook");
Memory::Write_U32(FS_EINVAL, command_address + 4);
return GetDefaultReply();
}
// IOCTL_STM_EVENTHOOK waits until the reset button or power button
// is pressed.
s_event_hook_address = command_address;
return GetNoReply();
}
void CWII_IPC_HLE_Device_stm_eventhook::TriggerEvent(const u32 event) const
{
if (!m_Active || s_event_hook_address == 0)
{
// If the device isn't open, ignore the button press.
return;
}
// The reset button returns STM_EVENT_RESET.
u32 buffer_out = Memory::Read_U32(s_event_hook_address + 0x18);
Memory::Write_U32(event, buffer_out);
// Fill in command buffer.
Memory::Write_U32(FS_SUCCESS, s_event_hook_address + 4);
Memory::Write_U32(IPC_REP_ASYNC, s_event_hook_address);
Memory::Write_U32(IPC_CMD_IOCTL, s_event_hook_address + 8);
// Generate a reply to the IPC command.
WII_IPC_HLE_Interface::EnqueueReply(s_event_hook_address);
s_event_hook_address = 0;
}
void CWII_IPC_HLE_Device_stm_eventhook::ResetButton() const
{
// The reset button returns STM_EVENT_RESET.
TriggerEvent(STM_EVENT_RESET);
}
void CWII_IPC_HLE_Device_stm_eventhook::PowerButton() const
{
TriggerEvent(STM_EVENT_POWER);
}

View File

@ -4,7 +4,6 @@
#pragma once
#include <string>
#include "Core/IPC_HLE/WII_IPC_HLE_Device.h"
enum
@ -32,155 +31,37 @@ enum
};
// The /dev/stm/immediate
class CWII_IPC_HLE_Device_stm_immediate : public IWII_IPC_HLE_Device
class CWII_IPC_HLE_Device_stm_immediate final : public IWII_IPC_HLE_Device
{
public:
CWII_IPC_HLE_Device_stm_immediate(u32 _DeviceID, const std::string& _rDeviceName)
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
CWII_IPC_HLE_Device_stm_immediate(u32 device_id, const std::string& device_name)
: IWII_IPC_HLE_Device(device_id, device_name)
{
}
virtual ~CWII_IPC_HLE_Device_stm_immediate() {}
IPCCommandResult Open(u32 _CommandAddress, u32 _Mode) override
{
INFO_LOG(WII_IPC_STM, "STM immediate: Open");
Memory::Write_U32(GetDeviceID(), _CommandAddress + 4);
m_Active = true;
return GetDefaultReply();
}
IPCCommandResult Close(u32 _CommandAddress, bool _bForce) override
{
INFO_LOG(WII_IPC_STM, "STM immediate: Close");
if (!_bForce)
Memory::Write_U32(0, _CommandAddress + 4);
m_Active = false;
return GetDefaultReply();
}
IPCCommandResult IOCtl(u32 _CommandAddress) override
{
u32 Parameter = Memory::Read_U32(_CommandAddress + 0x0C);
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);
// Prepare the out buffer(s) with zeroes as a safety precaution
// to avoid returning bad values
Memory::Memset(BufferOut, 0, BufferOutSize);
u32 ReturnValue = 0;
switch (Parameter)
{
case IOCTL_STM_RELEASE_EH:
INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str());
INFO_LOG(WII_IPC_STM, " IOCTL_STM_RELEASE_EH");
break;
case IOCTL_STM_HOTRESET:
INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str());
INFO_LOG(WII_IPC_STM, " IOCTL_STM_HOTRESET");
break;
case IOCTL_STM_VIDIMMING: // (Input: 20 bytes, Output: 20 bytes)
INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str());
INFO_LOG(WII_IPC_STM, " IOCTL_STM_VIDIMMING");
// DumpCommands(BufferIn, BufferInSize / 4, LogTypes::WII_IPC_STM);
// Memory::Write_U32(1, BufferOut);
// ReturnValue = 1;
break;
case IOCTL_STM_LEDMODE: // (Input: 20 bytes, Output: 20 bytes)
INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str());
INFO_LOG(WII_IPC_STM, " IOCTL_STM_LEDMODE");
break;
default:
{
_dbg_assert_msg_(WII_IPC_STM, 0, "CWII_IPC_HLE_Device_stm_immediate: 0x%x", Parameter);
INFO_LOG(WII_IPC_STM, "%s - IOCtl:", GetDeviceName().c_str());
DEBUG_LOG(WII_IPC_STM, " Parameter: 0x%x", Parameter);
DEBUG_LOG(WII_IPC_STM, " InBuffer: 0x%08x", BufferIn);
DEBUG_LOG(WII_IPC_STM, " InBufferSize: 0x%08x", BufferInSize);
DEBUG_LOG(WII_IPC_STM, " OutBuffer: 0x%08x", BufferOut);
DEBUG_LOG(WII_IPC_STM, " OutBufferSize: 0x%08x", BufferOutSize);
}
break;
}
// Write return value to the IPC call
Memory::Write_U32(ReturnValue, _CommandAddress + 0x4);
return GetDefaultReply();
}
~CWII_IPC_HLE_Device_stm_immediate() override = default;
IPCCommandResult Open(u32 command_address, u32 mode) override;
IPCCommandResult Close(u32 command_address, bool force) override;
IPCCommandResult IOCtl(u32 command_address) override;
};
// The /dev/stm/eventhook
class CWII_IPC_HLE_Device_stm_eventhook : public IWII_IPC_HLE_Device
class CWII_IPC_HLE_Device_stm_eventhook final : public IWII_IPC_HLE_Device
{
public:
CWII_IPC_HLE_Device_stm_eventhook(u32 _DeviceID, const std::string& _rDeviceName)
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName), m_EventHookAddress(0)
CWII_IPC_HLE_Device_stm_eventhook(u32 device_id, const std::string& device_name)
: IWII_IPC_HLE_Device(device_id, device_name)
{
}
virtual ~CWII_IPC_HLE_Device_stm_eventhook() {}
IPCCommandResult Open(u32 _CommandAddress, u32 _Mode) override
{
Memory::Write_U32(GetDeviceID(), _CommandAddress + 4);
m_Active = true;
return GetDefaultReply();
}
~CWII_IPC_HLE_Device_stm_eventhook() override = default;
IPCCommandResult Open(u32 command_address, u32 mode) override;
IPCCommandResult Close(u32 command_address, bool force) override;
IPCCommandResult IOCtl(u32 command_address) override;
IPCCommandResult Close(u32 _CommandAddress, bool _bForce) override
{
m_EventHookAddress = 0;
void ResetButton() const;
void PowerButton() const;
INFO_LOG(WII_IPC_STM, "STM eventhook: Close");
if (!_bForce)
Memory::Write_U32(0, _CommandAddress + 4);
m_Active = false;
return GetDefaultReply();
}
IPCCommandResult IOCtl(u32 _CommandAddress) override
{
u32 Parameter = Memory::Read_U32(_CommandAddress + 0x0C);
if (Parameter != IOCTL_STM_EVENTHOOK)
{
ERROR_LOG(WII_IPC_STM, "Bad IOCtl in CWII_IPC_HLE_Device_stm_eventhook");
Memory::Write_U32(FS_EINVAL, _CommandAddress + 4);
return GetDefaultReply();
}
// IOCTL_STM_EVENTHOOK waits until the reset button or power button
// is pressed.
m_EventHookAddress = _CommandAddress;
return GetNoReply();
}
void ResetButton()
{
if (!m_Active || m_EventHookAddress == 0)
{
// If the device isn't open, ignore the button press.
return;
}
// The reset button returns STM_EVENT_RESET.
u32 BufferOut = Memory::Read_U32(m_EventHookAddress + 0x18);
Memory::Write_U32(STM_EVENT_RESET, BufferOut);
// Fill in command buffer.
Memory::Write_U32(FS_SUCCESS, m_EventHookAddress + 4);
Memory::Write_U32(IPC_REP_ASYNC, m_EventHookAddress);
Memory::Write_U32(IPC_CMD_IOCTL, m_EventHookAddress + 8);
// Generate a reply to the IPC command.
WII_IPC_HLE_Interface::EnqueueReply(m_EventHookAddress);
}
// STATE_TO_SAVE
u32 m_EventHookAddress;
private:
void TriggerEvent(u32 event) const;
};

View File

@ -158,6 +158,7 @@ private:
bool m_bGameLoading = false;
bool m_bClosing = false;
bool m_confirmStop = false;
bool m_tried_graceful_shutdown = false;
int m_saveSlot = 1;
std::vector<std::string> drives;

View File

@ -1148,9 +1148,12 @@ void CFrame::DoStop()
Core::SetState(Core::CORE_PAUSE);
}
wxMessageDialog m_StopDlg(this, _("Do you want to stop the current emulation?"),
_("Please confirm..."),
wxYES_NO | wxSTAY_ON_TOP | wxICON_EXCLAMATION, wxDefaultPosition);
wxMessageDialog m_StopDlg(
this, !m_tried_graceful_shutdown ? _("Do you want to stop the current emulation?") :
_("A shutdown is already in progress. Unsaved data "
"may be lost if you stop the current emulation "
"before it completes. Force stop?"),
_("Please confirm..."), wxYES_NO | wxSTAY_ON_TOP | wxICON_EXCLAMATION, wxDefaultPosition);
HotkeyManagerEmu::Enable(false);
int Ret = m_StopDlg.ShowModal();
@ -1165,6 +1168,16 @@ void CFrame::DoStop()
}
}
if (SConfig::GetInstance().bWii && !m_tried_graceful_shutdown)
{
Core::DisplayMessage("Shutting down", 30000);
Core::SetState(Core::CORE_RUN);
ProcessorInterface::PowerButton_Tap();
m_confirmStop = false;
m_tried_graceful_shutdown = true;
return;
}
if (UseDebugger && g_pCodeWindow)
{
if (g_pCodeWindow->m_WatchWindow)
@ -1200,6 +1213,7 @@ void CFrame::DoStop()
void CFrame::OnStopped()
{
m_confirmStop = false;
m_tried_graceful_shutdown = false;
#if defined(HAVE_X11) && HAVE_X11
if (SConfig::GetInstance().bDisableScreenSaver)

View File

@ -34,6 +34,8 @@
static bool rendererHasFocus = true;
static bool rendererIsFullscreen = false;
static Common::Flag s_running{true};
static Common::Flag s_shutdown_requested{false};
static Common::Flag s_tried_graceful_shutdown{false};
static void signal_handler(int)
{
@ -41,7 +43,12 @@ static void signal_handler(int)
if (write(STDERR_FILENO, message, sizeof(message)) < 0)
{
}
s_running.Clear();
s_shutdown_requested.Set();
}
namespace ProcessorInterface
{
void PowerButton_Tap();
}
class Platform
@ -222,6 +229,19 @@ class PlatformX11 : public Platform
// The actual loop
while (s_running.IsSet())
{
if (s_shutdown_requested.TestAndClear())
{
if (!s_tried_graceful_shutdown.IsSet() && SConfig::GetInstance().bWii)
{
ProcessorInterface::PowerButton_Tap();
s_tried_graceful_shutdown.Set();
}
else
{
s_running.Clear();
}
}
XEvent event;
KeySym key;
for (int num_events = XPending(dpy); num_events > 0; num_events--)
@ -286,7 +306,7 @@ class PlatformX11 : public Platform
break;
case ClientMessage:
if ((unsigned long)event.xclient.data.l[0] == XInternAtom(dpy, "WM_DELETE_WINDOW", False))
s_running.Clear();
s_shutdown_requested.Set();
break;
}
}
@ -375,6 +395,7 @@ int main(int argc, char* argv[])
UICommon::SetUserDirectory(""); // Auto-detect user folder
UICommon::Init();
Core::SetOnStoppedCallback([]() { s_running.Clear(); });
platform->Init();
// Shut down cleanly on SIGINT and SIGTERM