mirror of
https://github.com/mikage-emu/mikage-dev.git
synced 2025-01-09 06:50:59 +01:00
287 lines
12 KiB
C++
287 lines
12 KiB
C++
#include "mcu.hpp"
|
|
#include "os.hpp"
|
|
|
|
#include "../platform/mcu.hpp"
|
|
|
|
#include <memory>
|
|
|
|
namespace HLE {
|
|
|
|
namespace OS {
|
|
|
|
FakeMCU::FakeMCU(FakeThread& thread)
|
|
: logger(*thread.GetLogger()) {
|
|
|
|
thread.name = "MCUMainThread";
|
|
|
|
OS::Result result;
|
|
|
|
std::tie(result,gpu_event) = thread.CallSVC(&OS::SVCCreateEvent, ResetType::OneShot);
|
|
if (result != RESULT_OK)
|
|
thread.CallSVC(&OS::SVCBreak, OS::BreakReason::Panic);
|
|
gpu_event.second->name = "mcu::GPUEvent";
|
|
|
|
std::tie(result,hid_event) = thread.CallSVC(&OS::SVCCreateEvent, ResetType::OneShot);
|
|
if (result != RESULT_OK)
|
|
thread.CallSVC(&OS::SVCBreak, OS::BreakReason::Panic);
|
|
hid_event.second->name = "mcu::HIDEvent";
|
|
|
|
auto hid_thread = std::make_shared<WrappedFakeThread>(thread.GetParentProcess(),
|
|
[this](FakeThread& thread) { return HIDThread(thread); });
|
|
hid_thread->name = "MCUHIDThread";
|
|
thread.GetParentProcess().AttachThread(hid_thread);
|
|
|
|
// TODO: Need mcu::PLS for ps LLE (the lack of that doesn't currently seem to block anything though)
|
|
|
|
GPUThread(thread);
|
|
}
|
|
|
|
void FakeMCU::GPUThread(FakeThread& thread) {
|
|
ServiceUtil service(thread, "mcu::GPU", 1);
|
|
|
|
Handle last_signalled = HANDLE_INVALID;
|
|
|
|
for (;;) {
|
|
OS::Result result;
|
|
int32_t index;
|
|
std::tie(result,index) = service.ReplyAndReceive(thread, last_signalled);
|
|
last_signalled = HANDLE_INVALID;
|
|
if (result != RESULT_OK)
|
|
thread.CallSVC(&OS::SVCBreak, OS::BreakReason::Panic);
|
|
|
|
if (index == 0) {
|
|
// ServerPort: Incoming client connection
|
|
|
|
int32_t session_index;
|
|
std::tie(result,session_index) = service.AcceptSession(thread, index);
|
|
if (result != RESULT_OK) {
|
|
auto session = service.GetObject<ServerSession>(session_index);
|
|
if (!session) {
|
|
logger.error("{}Failed to accept session.", ThreadPrinter{thread});
|
|
thread.CallSVC(&OS::SVCBreak, OS::BreakReason::Panic);
|
|
}
|
|
|
|
auto session_handle = service.GetHandle(session_index);
|
|
logger.warn("{}Failed to accept session. Maximal number of sessions exhausted? Closing session handle {}",
|
|
ThreadPrinter{thread}, HandlePrinter{thread,session_handle});
|
|
thread.CallSVC(&OS::SVCCloseHandle, session_handle);
|
|
}
|
|
} else {
|
|
// server_session: Incoming IPC command from the indexed client
|
|
logger.info("{}received IPC request", ThreadPrinter{thread});
|
|
Platform::IPC::CommandHeader header = { thread.ReadTLS(0x80) };
|
|
auto signalled_handle = service.GetHandle(index);
|
|
GPUCommandHandler(thread, signalled_handle, header);
|
|
last_signalled = signalled_handle;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FakeMCU::HIDThread(FakeThread& thread) {
|
|
ServiceUtil service(thread, "mcu::HID", 1);
|
|
|
|
Handle last_signalled = HANDLE_INVALID;
|
|
|
|
for (;;) {
|
|
OS::Result result;
|
|
int32_t index;
|
|
std::tie(result,index) = service.ReplyAndReceive(thread, last_signalled);
|
|
last_signalled = HANDLE_INVALID;
|
|
if (result != RESULT_OK)
|
|
thread.CallSVC(&OS::SVCBreak, OS::BreakReason::Panic);
|
|
|
|
if (index == 0) {
|
|
// ServerPort: Incoming client connection
|
|
|
|
int32_t session_index;
|
|
std::tie(result,session_index) = service.AcceptSession(thread, index);
|
|
if (result != RESULT_OK) {
|
|
auto session = service.GetObject<ServerSession>(session_index);
|
|
if (!session) {
|
|
logger.error("{}Failed to accept session.", ThreadPrinter{thread});
|
|
thread.CallSVC(&OS::SVCBreak, OS::BreakReason::Panic);
|
|
}
|
|
|
|
auto session_handle = service.GetHandle(session_index);
|
|
logger.warn("{}Failed to accept session. Maximal number of sessions exhausted? Closing session handle {}",
|
|
ThreadPrinter{thread}, HandlePrinter{thread,session_handle});
|
|
thread.CallSVC(&OS::SVCCloseHandle, session_handle);
|
|
}
|
|
} else {
|
|
// server_session: Incoming IPC command from the indexed client
|
|
logger.info("{}received IPC request", ThreadPrinter{thread});
|
|
Platform::IPC::CommandHeader header = { thread.ReadTLS(0x80) };
|
|
auto signalled_handle = service.GetHandle(index);
|
|
HIDCommandHandler(thread, signalled_handle, header);
|
|
last_signalled = signalled_handle;
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename Class, typename Func>
|
|
static auto BindMemFn(Func f, Class* c) {
|
|
return [f,c](auto&&... args) { return std::mem_fn(f)(c, args...); };
|
|
}
|
|
|
|
void FakeMCU::GPUCommandHandler(FakeThread& thread, Handle sender, const IPC::CommandHeader& header) try {
|
|
using namespace Platform::MCU::GPU;
|
|
|
|
switch (header.command_id) {
|
|
case GetLcdPowerState::id:
|
|
return IPC::HandleIPCCommand<GetLcdPowerState>(BindMemFn(&FakeMCU::GPUGetLcdPowerState, this), thread, thread);
|
|
|
|
case SetLcdPowerState::id:
|
|
return IPC::HandleIPCCommand<SetLcdPowerState>(BindMemFn(&FakeMCU::GPUSetLcdPowerState, this), thread, thread);
|
|
|
|
case GetGpuLcdInterfaceState::id:
|
|
return IPC::HandleIPCCommand<GetGpuLcdInterfaceState>(BindMemFn(&FakeMCU::GPUGetGpuLcdInterfaceState, this), thread, thread);
|
|
|
|
case SetGpuLcdInterfaceState::id:
|
|
return IPC::HandleIPCCommand<SetGpuLcdInterfaceState>(BindMemFn(&FakeMCU::GPUSetGpuLcdInterfaceState, this), thread, thread);
|
|
|
|
case GetMcuFwVerHigh::id:
|
|
return IPC::HandleIPCCommand<GetMcuFwVerHigh>(BindMemFn(&FakeMCU::GPUGetMcuFwVerHigh, this), thread, thread);
|
|
|
|
case GetMcuFwVerLow::id:
|
|
return IPC::HandleIPCCommand<GetMcuFwVerLow>(BindMemFn(&FakeMCU::GPUGetMcuFwVerLow, this), thread, thread);
|
|
|
|
case Set3dLedState::id:
|
|
return IPC::HandleIPCCommand<Set3dLedState>(BindMemFn(&FakeMCU::GPUSet3dLedState, this), thread, thread);
|
|
|
|
case GetMcuGpuEvent::id:
|
|
return IPC::HandleIPCCommand<GetMcuGpuEvent>(BindMemFn(&FakeMCU::GPUGetMcuGpuEvent, this), thread, thread);
|
|
|
|
case GetMcuGpuEventReason::id:
|
|
return IPC::HandleIPCCommand<GetMcuGpuEventReason>(BindMemFn(&FakeMCU::GPUGetMcuGpuEventReason, this), thread, thread);
|
|
|
|
default:
|
|
throw IPC::IPCError{header.raw, 0xdeadbeef};
|
|
}
|
|
} catch (IPC::IPCError& err) {
|
|
throw std::runtime_error(fmt::format("Unknown mcu::GPU command request with header {:#010x}", err.header));
|
|
}
|
|
|
|
void FakeMCU::HIDCommandHandler(FakeThread& thread, Handle sender, const IPC::CommandHeader& header) try {
|
|
using namespace Platform::MCU::HID;
|
|
|
|
switch (header.command_id) {
|
|
case Unknown0x1::id:
|
|
return IPC::HandleIPCCommand<Unknown0x1>(BindMemFn(&FakeMCU::HIDUnknown0x1, this), thread, thread);
|
|
|
|
case GetMcuHidEventHandle::id:
|
|
return IPC::HandleIPCCommand<GetMcuHidEventHandle>(BindMemFn(&FakeMCU::HIDGetMcuHidEventHandle, this), thread, thread);
|
|
|
|
case Get3dSliderState::id:
|
|
return IPC::HandleIPCCommand<Get3dSliderState>(BindMemFn(&FakeMCU::HIDGet3dSliderState, this), thread, thread);
|
|
|
|
case SetAccelerometerState::id:
|
|
return IPC::HandleIPCCommand<SetAccelerometerState>(BindMemFn(&FakeMCU::HIDSetAccelerometerState, this), thread, thread);
|
|
default:
|
|
throw IPC::IPCError{header.raw, 0xdeadbeef};
|
|
}
|
|
} catch (const IPC::IPCError& err) {
|
|
throw std::runtime_error(fmt::format("Unknown mcu::HID command request with header {:#010x}", err.header));
|
|
}
|
|
|
|
OS::ResultAnd<uint32_t, uint32_t> FakeMCU::GPUGetLcdPowerState(FakeThread& thread) {
|
|
logger.info("{}received GetLcdPowerState", ThreadPrinter{thread});
|
|
return std::make_tuple(RESULT_OK, uint32_t{0}, uint32_t{0});
|
|
}
|
|
|
|
OS::ResultAnd<> FakeMCU::GPUSetLcdPowerState(FakeThread& thread, uint32_t param1, uint32_t param2) {
|
|
logger.info("{}received SetLcdPowerState, param1={:#010x}, param2={:#010x}",
|
|
ThreadPrinter{thread}, param1, param2);
|
|
return std::make_tuple(RESULT_OK);
|
|
}
|
|
|
|
OS::ResultAnd<uint32_t> FakeMCU::GPUGetGpuLcdInterfaceState(FakeThread& thread) {
|
|
logger.info("{}received GetGpuLcdInterfaceState", ThreadPrinter{thread});
|
|
return std::make_tuple(RESULT_OK, uint32_t{0});
|
|
}
|
|
|
|
OS::ResultAnd<> FakeMCU::GPUSetGpuLcdInterfaceState(FakeThread& thread, uint32_t state) {
|
|
logger.info("{}received SetGpuLcdInterfaceState, state={:#010x}", ThreadPrinter{thread}, state);
|
|
return std::make_tuple(RESULT_OK);
|
|
}
|
|
|
|
OS::ResultAnd<uint32_t> FakeMCU::GPUGetMcuFwVerHigh(FakeThread& thread) {
|
|
logger.info("{}received GetMcuFwVerHigh", ThreadPrinter{thread});
|
|
return std::make_tuple(RESULT_OK, uint32_t{/*0x4000*/2});
|
|
}
|
|
|
|
OS::ResultAnd<uint32_t> FakeMCU::GPUGetMcuFwVerLow(FakeThread& thread) {
|
|
logger.info("{}received GetMcuFwVerLow", ThreadPrinter{thread});
|
|
return std::make_tuple(RESULT_OK, uint32_t{/*0x4000*/0x26});
|
|
}
|
|
|
|
OS::ResultAnd<> FakeMCU::GPUSet3dLedState(FakeThread& thread, uint32_t state) {
|
|
logger.info("{}received Set3dLedState: state={:#x}", ThreadPrinter{thread}, state);
|
|
return std::make_tuple(RESULT_OK);
|
|
}
|
|
|
|
OS::ResultAnd<Handle> FakeMCU::GPUGetMcuGpuEvent(FakeThread& thread) {
|
|
logger.info("{}received GetMcuGpuEvent", ThreadPrinter{thread});
|
|
|
|
// let's immediately signal the event. GSP seems to use it to detect whether the GPU is ready?
|
|
// TODO: Race condition right here! We may be signalling this event too early
|
|
gpu_event.second->SignalEvent();
|
|
return std::make_tuple(RESULT_OK, gpu_event.first);
|
|
}
|
|
|
|
OS::ResultAnd<uint32_t> FakeMCU::GPUGetMcuGpuEventReason(FakeThread& thread) {
|
|
logger.info("{}received GetMcuGpuEventReason", ThreadPrinter{thread});
|
|
|
|
// TODO: This is just some stub code to make the GSP module work properly
|
|
// without an actual MCU implementation.
|
|
uint32_t event_reason = 0;
|
|
if (invocation == 0) {
|
|
// LCD turned on
|
|
event_reason = 0x02000000;
|
|
gpu_event.second->SignalEvent();
|
|
} else if (invocation == 1) {
|
|
// LCD backlight turned on (both top and bottom screens).
|
|
// GSP expects both of these to be signaled simultaneously
|
|
event_reason = 0x28000000;
|
|
} else {
|
|
throw std::runtime_error("Cannot request more than two reasons");
|
|
event_reason = 0;
|
|
}
|
|
++invocation;
|
|
|
|
return std::make_tuple(RESULT_OK, event_reason);
|
|
}
|
|
|
|
OS::ResultAnd<> FakeMCU::HIDUnknown0x1(FakeThread& thread, uint32_t param) {
|
|
logger.info("{}received Unknown0x1, param={:#010x}", ThreadPrinter{thread}, param);
|
|
return std::make_tuple(RESULT_OK);
|
|
}
|
|
|
|
OS::ResultAnd<Handle> FakeMCU::HIDGetMcuHidEventHandle(FakeThread& thread) {
|
|
logger.info("{}received GetMcuHidEventHandle", ThreadPrinter{thread});
|
|
|
|
// Let's immediately signal the event.
|
|
// TODO: Remove this code and implement this module properly instead!
|
|
// TODO: At least, we should call SVCSignalEvent here...
|
|
hid_event.second->SignalEvent();
|
|
|
|
return std::make_tuple(RESULT_OK, hid_event.first);
|
|
}
|
|
|
|
OS::ResultAnd<uint32_t> FakeMCU::HIDGet3dSliderState(FakeThread& thread) {
|
|
logger.info("{}received Get3dSliderState", ThreadPrinter{thread});
|
|
|
|
// Not sure what format this is in. HID exposes floats, but it seems unlikely that this is the actual hardware representation
|
|
// TODO: Actually read this from I2C device 3
|
|
return std::make_tuple(RESULT_OK, uint32_t{0});
|
|
}
|
|
|
|
OS::ResultAnd<> FakeMCU::HIDSetAccelerometerState(FakeThread& thread, uint32_t state) {
|
|
logger.info("{}received SetAccelerometerState (state={:#x})",
|
|
ThreadPrinter{thread}, state);
|
|
|
|
return std::make_tuple(RESULT_OK);
|
|
}
|
|
|
|
} // namespace OS
|
|
|
|
} // namespace HLE
|