diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp index fdbf6d159..e2ed1d157 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp @@ -120,7 +120,46 @@ namespace ams::htc::server { } void HtcmiscImpl::ServerThread() { - AMS_ABORT("HtcmiscImpl::ServerThread"); + /* Loop so long as we're not cancelled. */ + while (!m_cancelled) { + /* Open the rpc server. */ + m_rpc_server.Open(); + + /* Ensure we close, if something goes wrong. */ + auto server_guard = SCOPE_GUARD { m_rpc_server.Close(); }; + + /* Wait for the rpc server. */ + if (m_rpc_server.WaitAny(htclow::ChannelState_Connectable, m_cancel_event.GetBase()) != 0) { + break; + } + + /* Start the rpc server. */ + if (R_FAILED(m_rpc_server.Start())) { + break; + } + + /* We're connected! */ + this->SetServerConnectionEvent(true); + server_guard.Cancel(); + + /* We're connected, so we want to cleanup when we're done. */ + ON_SCOPE_EXIT { + m_rpc_server.Close(); + m_rpc_server.Cancel(); + m_rpc_server.Wait(); + }; + + /* Wait to become disconnected. */ + if (m_rpc_server.WaitAny(htclow::ChannelState_Disconnected, m_cancel_event.GetBase()) != 0) { + break; + } + + /* Set ourselves as disconnected. */ + this->SetServerConnectionEvent(false); + } + + /* Set ourselves as disconnected. */ + this->SetServerConnectionEvent(false); } void HtcmiscImpl::SetClientConnectionEvent(bool en) { diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp index 438df31e2..5e28c1255 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.cpp @@ -37,4 +37,143 @@ namespace ams::htc::server::rpc { /* ... */ } + void HtcmiscRpcServer::Open() { + R_ABORT_UNLESS(m_driver->Open(m_channel_id, m_driver_receive_buffer, sizeof(m_driver_receive_buffer), m_driver_send_buffer, sizeof(m_driver_send_buffer))); + } + + void HtcmiscRpcServer::Close() { + m_driver->Close(m_channel_id); + } + + Result HtcmiscRpcServer::Start() { + /* Connect. */ + R_TRY(m_driver->Connect(m_channel_id)); + + /* Create our thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_receive_thread), ReceiveThreadEntry, this, m_receive_thread_stack, ReceiveThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcmiscReceive))); + + /* Set thread name pointer. */ + os::SetThreadNamePointer(std::addressof(m_receive_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcmiscReceive)); + + /* Start thread. */ + os::StartThread(std::addressof(m_receive_thread)); + + /* Set initial state. */ + m_cancelled = false; + m_thread_running = true; + + return ResultSuccess(); + } + + void HtcmiscRpcServer::Cancel() { + /* Set cancelled. */ + m_cancelled = true; + } + + void HtcmiscRpcServer::Wait() { + /* Wait for thread to not be running. */ + if (m_thread_running) { + os::WaitThread(std::addressof(m_receive_thread)); + os::DestroyThread(std::addressof(m_receive_thread)); + } + m_thread_running = false; + } + + int HtcmiscRpcServer::WaitAny(htclow::ChannelState state, os::EventType *event) { + /* Check if we're already signaled. */ + if (os::TryWaitEvent(event)) { + return 1; + } + if (m_driver->GetChannelState(m_channel_id) == state) { + return 0; + } + + /* Wait. */ + while (true) { + const auto idx = os::WaitAny(m_driver->GetChannelStateEvent(m_channel_id), event); + if (idx == 0) { + if (m_driver->GetChannelState(m_channel_id) == state) { + return 0; + } + } else { + return idx; + } + } + } + + Result HtcmiscRpcServer::ReceiveThread() { + /* Loop forever. */ + auto *header = reinterpret_cast(m_receive_buffer); + while (true) { + /* Try to receive a packet header. */ + R_TRY(this->ReceiveHeader(header)); + + /* Track how much we've received. */ + size_t received = sizeof(*header); + + /* If the packet has one, receive its body. */ + if (header->body_size > 0) { + /* Sanity check the body size. */ + AMS_ABORT_UNLESS(util::IsIntValueRepresentable(header->body_size)); + AMS_ABORT_UNLESS(static_cast(header->body_size) <= sizeof(m_receive_buffer) - received); + + /* Receive the body. */ + R_TRY(this->ReceiveBody(header->data, header->body_size)); + + /* Note that we received the body. */ + received += header->body_size; + } + + /* Check that the packet is a request packet. */ + R_UNLESS(header->category == HtcmiscPacketCategory::Request, htc::ResultInvalidCategory()); + + /* Handle specific requests. */ + if (header->type == HtcmiscPacketType::SetTargetName) { + R_TRY(this->ProcessSetTargetNameRequest(header->data, header->body_size, header->task_id)); + } + } + } + + Result HtcmiscRpcServer::ProcessSetTargetNameRequest(const char *name, size_t size, u32 task_id) { + /* TODO: we need to use settings::fwdbg::SetSettingsItemValue here, but this will require ams support for set:fd re-enable? */ + /* Needs some thought. */ + AMS_ABORT("HtcmiscRpcServer::ProcessSetTargetNameRequest"); + } + + Result HtcmiscRpcServer::ReceiveHeader(HtcmiscRpcPacket *header) { + /* Receive. */ + s64 received; + R_TRY(m_driver->Receive(std::addressof(received), reinterpret_cast(header), sizeof(*header), m_channel_id, htclow::ReceiveOption_ReceiveAllData)); + + /* Check size. */ + R_UNLESS(static_cast(received) == sizeof(*header), htc::ResultInvalidSize()); + + return ResultSuccess(); + } + + Result HtcmiscRpcServer::ReceiveBody(char *dst, size_t size) { + /* Receive. */ + s64 received; + R_TRY(m_driver->Receive(std::addressof(received), dst, size, m_channel_id, htclow::ReceiveOption_ReceiveAllData)); + + /* Check size. */ + R_UNLESS(static_cast(received) == size, htc::ResultInvalidSize()); + + return ResultSuccess(); + } + + Result HtcmiscRpcServer::SendRequest(const char *src, size_t size) { + /* Sanity check our size. */ + AMS_ASSERT(util::IsIntValueRepresentable(size)); + + /* Send the data. */ + s64 sent; + R_TRY(m_driver->Send(std::addressof(sent), src, static_cast(size), m_channel_id)); + + /* Check that we sent the right amount. */ + R_UNLESS(sent == static_cast(size), htc::ResultInvalidSize()); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp index 71d028130..598559afe 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_htcmisc_rpc_server.hpp @@ -16,10 +16,14 @@ #pragma once #include #include "../driver/htc_i_driver.hpp" +#include "htc_htcmisc_rpc_tasks.hpp" namespace ams::htc::server::rpc { class HtcmiscRpcServer { + private: + /* TODO: where is this value coming from, again? */ + static constexpr size_t BufferSize = 1_KB; private: u64 m_00; driver::IDriver *m_driver; @@ -28,8 +32,31 @@ namespace ams::htc::server::rpc { os::ThreadType m_receive_thread; bool m_cancelled; bool m_thread_running; + char m_receive_buffer[BufferSize]; + char m_send_buffer[BufferSize]; + u8 m_driver_receive_buffer[4_KB]; + u8 m_driver_send_buffer[4_KB]; + private: + static void ReceiveThreadEntry(void *arg) { static_cast(arg)->ReceiveThread(); } + + Result ReceiveThread(); public: HtcmiscRpcServer(driver::IDriver *driver, htclow::ChannelId channel); + public: + void Open(); + void Close(); + + Result Start(); + void Cancel(); + void Wait(); + + int WaitAny(htclow::ChannelState state, os::EventType *event); + private: + Result ProcessSetTargetNameRequest(const char *name, size_t size, u32 task_id); + + Result ReceiveHeader(HtcmiscRpcPacket *header); + Result ReceiveBody(char *dst, size_t size); + Result SendRequest(const char *src, size_t size); }; }