diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp index 4fc86daf9..fdbf6d159 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.cpp @@ -77,11 +77,83 @@ namespace ams::htc::server { } void HtcmiscImpl::ClientThread() { - AMS_ABORT("HtcmiscImpl::ClientThread"); + /* Loop so long as we're not cancelled. */ + while (!m_cancelled) { + /* Open the rpc client. */ + m_rpc_client.Open(); + + /* Ensure we close, if something goes wrong. */ + auto client_guard = SCOPE_GUARD { m_rpc_client.Close(); }; + + /* Wait for the rpc client. */ + if (m_rpc_client.WaitAny(htclow::ChannelState_Connectable, m_cancel_event.GetBase()) != 0) { + break; + } + + /* Start the rpc client. */ + if (R_FAILED(m_rpc_client.Start())) { + break; + } + + /* We're connected! */ + this->SetClientConnectionEvent(true); + client_guard.Cancel(); + + /* We're connected, so we want to cleanup when we're done. */ + ON_SCOPE_EXIT { + m_rpc_client.Close(); + m_rpc_client.Cancel(); + m_rpc_client.Wait(); + }; + + /* Wait to become disconnected. */ + if (m_rpc_client.WaitAny(htclow::ChannelState_Disconnected, m_cancel_event.GetBase()) != 0) { + break; + } + + /* Set ourselves as disconnected. */ + this->SetClientConnectionEvent(false); + } + + /* Set ourselves as disconnected. */ + this->SetClientConnectionEvent(false); } void HtcmiscImpl::ServerThread() { AMS_ABORT("HtcmiscImpl::ServerThread"); } + void HtcmiscImpl::SetClientConnectionEvent(bool en) { + /* Lock ourselves. */ + std::scoped_lock lk(m_connection_mutex); + + /* Update our state. */ + if (m_client_connected != en) { + m_client_connected = en; + this->UpdateConnectionEvent(); + } + } + + void HtcmiscImpl::SetServerConnectionEvent(bool en) { + /* Lock ourselves. */ + std::scoped_lock lk(m_connection_mutex); + + /* Update our state. */ + if (m_server_connected != en) { + m_server_connected = en; + this->UpdateConnectionEvent(); + } + } + + void HtcmiscImpl::UpdateConnectionEvent() { + /* Determine if we're connected. */ + const bool connected = m_client_connected && m_server_connected; + + /* Update our state. */ + if (m_connected != connected) { + m_connected = connected; + m_connection_event.Signal(); + } + } + } diff --git a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp index 7cd407f1f..c012a9078 100644 --- a/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp +++ b/libraries/libstratosphere/source/htc/server/htc_htcmisc_impl.hpp @@ -47,6 +47,11 @@ namespace ams::htc::server { public: HtcmiscImpl(htclow::HtclowManager *htclow_manager); ~HtcmiscImpl(); + private: + void SetClientConnectionEvent(bool en); + void SetServerConnectionEvent(bool en); + + void UpdateConnectionEvent(); public: void Cancel(); /* TODO */ diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp index 6888c5539..f307c3d82 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.cpp @@ -53,4 +53,113 @@ namespace ams::htc::server::rpc { } } + void RpcClient::Open() { + R_ABORT_UNLESS(m_driver->Open(m_channel_id)); + } + + void RpcClient::Close() { + m_driver->Close(m_channel_id); + } + + Result RpcClient::Start() { + /* Connect. */ + R_TRY(m_driver->Connect(m_channel_id)); + + /* Initialize our task queue. */ + m_task_queue.Initialize(); + + /* Create our threads. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_receive_thread), ReceiveThreadEntry, this, m_receive_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcmiscReceive))); + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_send_thread), SendThreadEntry, this, m_send_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcmiscSend))); + + /* Set thread name pointers. */ + os::SetThreadNamePointer(std::addressof(m_receive_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcmiscReceive)); + os::SetThreadNamePointer(std::addressof(m_send_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcmiscSend)); + + /* Start threads. */ + os::StartThread(std::addressof(m_receive_thread)); + os::StartThread(std::addressof(m_send_thread)); + + /* Set initial state. */ + m_cancelled = false; + m_thread_running = true; + + /* Clear events. */ + for (size_t i = 0; i < MaxRpcCount; ++i) { + os::ClearEvent(std::addressof(m_receive_buffer_available_events[i])); + os::ClearEvent(std::addressof(m_send_buffer_available_events[i])); + } + + return ResultSuccess(); + } + + void RpcClient::Cancel() { + /* Set cancelled. */ + m_cancelled = true; + + /* Signal all events. */ + for (size_t i = 0; i < MaxRpcCount; ++i) { + os::SignalEvent(std::addressof(m_receive_buffer_available_events[i])); + os::SignalEvent(std::addressof(m_send_buffer_available_events[i])); + } + + /* Cancel our queue. */ + m_task_queue.Cancel(); + } + + void RpcClient::Wait() { + /* Wait for thread to not be running. */ + if (m_thread_running) { + os::WaitThread(std::addressof(m_receive_thread)); + os::WaitThread(std::addressof(m_send_thread)); + os::DestroyThread(std::addressof(m_receive_thread)); + os::DestroyThread(std::addressof(m_send_thread)); + } + m_thread_running = false; + + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Finalize the task queue. */ + m_task_queue.Finalize(); + + /* Cancel all tasks. */ + for (size_t i = 0; i < MaxRpcCount; ++i) { + if (m_task_active[i]) { + /* TODO: enum member */ + m_task_table.Get(i)->Cancel(static_cast(2)); + } + } + } + + int RpcClient::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; + } + } + } + + void RpcClient::ReceiveThread() { + AMS_ABORT("RpcClient::ReceiveThread"); + } + + void RpcClient::SendThread() { + AMS_ABORT("RpcClient::SendThread"); + } + } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp index 1a20439dc..e517025f6 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_client.hpp @@ -45,8 +45,23 @@ namespace ams::htc::server::rpc { os::EventType m_send_buffer_available_events[MaxRpcCount]; u8 m_receive_buffer[BufferSize]; u8 m_send_buffer[BufferSize]; + private: + static void ReceiveThreadEntry(void *arg) { static_cast(arg)->ReceiveThread(); } + static void SendThreadEntry(void *arg) { static_cast(arg)->SendThread(); } + + void ReceiveThread(); + void SendThread(); public: RpcClient(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); }; } diff --git a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp index ee75670c2..434791ee8 100644 --- a/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp +++ b/libraries/libstratosphere/source/htc/server/rpc/htc_rpc_task_id_free_list.hpp @@ -38,7 +38,7 @@ namespace ams::htc::server::rpc { /* Get index. */ const auto index = m_offset; - m_free_count = (m_free_count + 1) % MaxRpcCount; + m_offset = (m_offset + 1) % MaxRpcCount; --m_free_count; /* Get the task id. */