From 2341f18edda75ef5de7df0a1b97e5ae3d5a78985 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 05:45:23 -0800 Subject: [PATCH] htc: implement htclow listener thread --- .../htclow/htclow_channel_types.hpp | 21 +++ .../htclow/ctrl/htclow_ctrl_service.cpp | 56 ++++++++ .../htclow/ctrl/htclow_ctrl_service.hpp | 7 + .../source/htclow/ctrl/htclow_ctrl_state.hpp | 110 ++++++++++++++- .../htclow/ctrl/htclow_ctrl_state_machine.cpp | 125 +++++++++++++++++- .../htclow/ctrl/htclow_ctrl_state_machine.hpp | 21 +++ .../source/htclow/htclow_listener.cpp | 31 ++++- .../source/htclow/htclow_packet.hpp | 11 ++ .../source/htclow/htclow_packet_factory.cpp | 25 ++++ .../source/htclow/htclow_packet_factory.hpp | 3 + .../source/htclow/htclow_worker.cpp | 67 +++++++++- .../source/htclow/htclow_worker.hpp | 23 +++- .../source/htclow/mux/htclow_mux.cpp | 25 ++++ .../source/htclow/mux/htclow_mux.hpp | 3 + .../htclow/mux/htclow_mux_channel_impl.cpp | 56 ++++++++ .../htclow/mux/htclow_mux_channel_impl.hpp | 10 +- .../htclow/mux/htclow_mux_send_buffer.cpp | 9 ++ .../htclow/mux/htclow_mux_send_buffer.hpp | 2 + .../htclow/mux/htclow_mux_task_manager.cpp | 50 +++++++ .../htclow/mux/htclow_mux_task_manager.hpp | 23 +++- .../vapours/results/htclow_results.hpp | 2 + 21 files changed, 669 insertions(+), 11 deletions(-) create mode 100644 libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp create mode 100644 libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp index c72487fdb..bf60fd44b 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp @@ -27,4 +27,25 @@ namespace ams::htclow { ChannelId _channel_id; }; + enum ChannelState { + ChannelState_Connectable = 0, + ChannelState_Unconnectable = 1, + ChannelState_Connected = 2, + ChannelState_Shutdown = 3, + }; + + constexpr bool IsStateTransitionAllowed(ChannelState from, ChannelState to) { + switch (from) { + case ChannelState_Connectable: + return to == ChannelState_Unconnectable || to == ChannelState_Connected || to == ChannelState_Shutdown; + case ChannelState_Unconnectable: + return to == ChannelState_Connectable || to == ChannelState_Shutdown; + case ChannelState_Connected: + return to == ChannelState_Shutdown; + case ChannelState_Shutdown: + return to == ChannelState_Shutdown; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index 546f9fb1b..756ba4ba6 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -15,6 +15,8 @@ */ #include #include "htclow_ctrl_service.hpp" +#include "htclow_ctrl_state.hpp" +#include "htclow_ctrl_state_machine.hpp" #include "../mux/htclow_mux.hpp" namespace ams::htclow::ctrl { @@ -81,4 +83,58 @@ namespace ams::htclow::ctrl { this->UpdateBeaconResponse(this->GetConnectionType(driver_type)); } + Result HtcctrlService::NotifyDriverConnected() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + if (m_state_machine->GetHtcctrlState() == HtcctrlState_7) { + R_TRY(this->SetState(HtcctrlState_8)); + } else { + R_TRY(this->SetState(HtcctrlState_0)); + } + + return ResultSuccess(); + } + + Result HtcctrlService::NotifyDriverDisconnected() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + if (m_state_machine->GetHtcctrlState() == HtcctrlState_6) { + R_TRY(this->SetState(HtcctrlState_7)); + } else { + R_TRY(this->SetState(HtcctrlState_11)); + } + + return ResultSuccess(); + } + + Result HtcctrlService::SetState(HtcctrlState state) { + /* Set the state. */ + bool did_transition; + R_TRY(m_state_machine->SetHtcctrlState(std::addressof(did_transition), state)); + + /* Reflect the state transition, if one occurred. */ + if (did_transition) { + this->ReflectState(); + } + + return ResultSuccess(); + } + + void HtcctrlService::ReflectState() { + /* If our connected status changed, update. */ + if (m_state_machine->IsConnectedStatusChanged()) { + m_mux->UpdateChannelState(); + } + + /* If our sleeping status changed, update. */ + if (m_state_machine->IsSleepingStatusChanged()) { + m_mux->UpdateMuxState(); + } + + /* Broadcast our state transition. */ + m_condvar.Broadcast(); + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp index c6d208325..5d83d5698 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -17,6 +17,7 @@ #include #include "htclow_ctrl_settings_holder.hpp" #include "htclow_ctrl_send_buffer.hpp" +#include "htclow_ctrl_state.hpp" namespace ams::htclow { @@ -55,10 +56,16 @@ namespace ams::htclow::ctrl { const char *GetConnectionType(impl::DriverType driver_type) const; void UpdateBeaconResponse(const char *connection); + + Result SetState(HtcctrlState state); + void ReflectState(); public: HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux); void SetDriverType(impl::DriverType driver_type); + + Result NotifyDriverConnected(); + Result NotifyDriverDisconnected(); }; } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp index 9d739758a..790363f47 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state.hpp @@ -19,7 +19,115 @@ namespace ams::htclow::ctrl { enum HtcctrlState : u32 { - HtcctrlState_Eleven = 11, + HtcctrlState_0 = 0, + HtcctrlState_1 = 1, + HtcctrlState_2 = 2, + HtcctrlState_3 = 3, + HtcctrlState_4 = 4, + HtcctrlState_5 = 5, + HtcctrlState_6 = 6, + HtcctrlState_7 = 7, + HtcctrlState_8 = 8, + HtcctrlState_9 = 9, + HtcctrlState_10 = 10, + HtcctrlState_11 = 11, + HtcctrlState_12 = 12, }; + constexpr bool IsStateTransitionAllowed(HtcctrlState from, HtcctrlState to) { + switch (from) { + case HtcctrlState_0: + return to == HtcctrlState_1 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_1: + return to == HtcctrlState_2 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_2: + return to == HtcctrlState_3 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_3: + return to == HtcctrlState_4 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_4: + return to == HtcctrlState_5 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_5: + return to == HtcctrlState_6 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_6: + return to == HtcctrlState_7 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_7: + return to == HtcctrlState_8; + case HtcctrlState_8: + return to == HtcctrlState_9 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_9: + return to == HtcctrlState_4 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_10: + return to == HtcctrlState_1 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + case HtcctrlState_11: + return to == HtcctrlState_0; + case HtcctrlState_12: + return to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + constexpr bool IsDisconnected(HtcctrlState state) { + switch (state) { + case HtcctrlState_10: + case HtcctrlState_11: + return true; + default: + return false; + } + } + + constexpr bool IsConnecting(HtcctrlState state) { + switch (state) { + case HtcctrlState_0: + case HtcctrlState_1: + case HtcctrlState_10: + return true; + default: + return false; + } + } + + constexpr bool IsConnected(HtcctrlState state) { + switch (state) { + case HtcctrlState_2: + case HtcctrlState_3: + case HtcctrlState_4: + case HtcctrlState_5: + case HtcctrlState_6: + case HtcctrlState_7: + case HtcctrlState_8: + case HtcctrlState_9: + return true; + default: + return false; + } + } + + constexpr bool IsReadied(HtcctrlState state) { + switch (state) { + case HtcctrlState_4: + case HtcctrlState_5: + case HtcctrlState_6: + case HtcctrlState_7: + case HtcctrlState_8: + case HtcctrlState_9: + return true; + default: + return false; + } + } + + constexpr bool IsSleeping(HtcctrlState state) { + switch (state) { + case HtcctrlState_5: + case HtcctrlState_6: + case HtcctrlState_7: + case HtcctrlState_8: + case HtcctrlState_9: + return true; + default: + return false; + } + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp index b740a4bad..62a8297ea 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp @@ -42,7 +42,7 @@ namespace ams::htclow::ctrl { } - HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_Eleven), m_prev_state(HtcctrlState_Eleven), m_mutex() { + HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_11), m_prev_state(HtcctrlState_11), m_mutex() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -55,4 +55,127 @@ namespace ams::htclow::ctrl { } } + HtcctrlState HtcctrlStateMachine::GetHtcctrlState() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return m_state; + } + + Result HtcctrlStateMachine::SetHtcctrlState(bool *out_transitioned, HtcctrlState state) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Check that the transition is allowed. */ + R_UNLESS(ctrl::IsStateTransitionAllowed(m_state, state), htclow::ResultStateTransitionNotAllowed()); + + /* Get the state pre-transition. */ + const auto old_state = m_state; + + /* Set the state. */ + this->SetStateWithoutCheckInternal(state); + + /* Note whether we transitioned. */ + *out_transitioned = state != old_state; + return ResultSuccess(); + } + + bool HtcctrlStateMachine::IsConnected() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsConnected(m_state); + } + + bool HtcctrlStateMachine::IsReadied() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsReadied(m_state); + } + + bool HtcctrlStateMachine::IsUnconnectable() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return !ctrl::IsConnected(m_state); + } + + bool HtcctrlStateMachine::IsDisconnected() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsDisconnected(m_state); + } + + bool HtcctrlStateMachine::IsSleeping() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsSleeping(m_state); + } + + bool HtcctrlStateMachine::IsConnectedStatusChanged() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsConnected(m_prev_state) ^ ctrl::IsConnected(m_state); + } + + bool HtcctrlStateMachine::IsSleepingStatusChanged() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return ctrl::IsSleeping(m_prev_state) ^ ctrl::IsSleeping(m_state); + } + + void HtcctrlStateMachine::SetStateWithoutCheckInternal(HtcctrlState state) { + if (m_state != state) { + /* Clear service channel states, if we should. */ + if (ctrl::IsDisconnected(state)) { + this->ClearServiceChannelStates(); + } + + /* Transition our state. */ + m_prev_state = m_state; + m_state = state; + } + } + + void HtcctrlStateMachine::ClearServiceChannelStates() { + /* Clear all values in our map. */ + for (auto &pair : m_map) { + pair.second = {}; + } + } + + bool HtcctrlStateMachine::IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* TODO: What do these values mean? */ + auto it = m_map.find(channel); + return it != m_map.end() && it->second._04 == 2 && it->second._00 == 2; + } + + bool HtcctrlStateMachine::IsConnectable(const impl::ChannelInternalType &channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* TODO: What do these values mean? */ + auto it = m_map.find(channel); + return ctrl::IsConnected(m_state) && (!(it != m_map.end()) || it->second._04 != 2); + } + + void HtcctrlStateMachine::SetNotConnecting(const impl::ChannelInternalType &channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* TODO: What do these values mean? */ + auto it = m_map.find(channel); + if (it != m_map.end() && it->second._04 != 2) { + it->second._04 = 0; + } + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp index 10053e793..ad53a2d56 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp @@ -39,6 +39,27 @@ namespace ams::htclow::ctrl { os::SdkMutex m_mutex; public: HtcctrlStateMachine(); + + HtcctrlState GetHtcctrlState(); + Result SetHtcctrlState(bool *out_transitioned, HtcctrlState state); + + bool IsConnectedStatusChanged(); + bool IsSleepingStatusChanged(); + + bool IsConnected(); + bool IsReadied(); + bool IsUnconnectable(); + bool IsDisconnected(); + bool IsSleeping(); + + bool IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel); + bool IsConnectable(const impl::ChannelInternalType &channel); + + void SetNotConnecting(const impl::ChannelInternalType &channel); + private: + void SetStateWithoutCheckInternal(HtcctrlState state); + + void ClearServiceChannelStates(); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.cpp b/libraries/libstratosphere/source/htclow/htclow_listener.cpp index ac2d0a9b4..278cfc7cb 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.cpp @@ -56,8 +56,35 @@ namespace ams::htclow { } void Listener::ListenThread() { - /* TODO */ - AMS_ABORT("Listener::ListenThread"); + /* Check pre-conditions. */ + AMS_ASSERT(m_driver != nullptr); + AMS_ASSERT(m_worker != nullptr); + + /* Set the worker's driver. */ + m_worker->SetDriver(m_driver); + + /* Loop forever, while we're not cancelled. */ + while (!m_cancelled) { + /* Connect. */ + if (R_FAILED(m_driver->Connect(m_event.GetBase()))) { + return; + } + + /* Notify that we're connected. */ + R_ABORT_UNLESS(m_service->NotifyDriverConnected()); + + /* Start the worker. */ + m_worker->Start(); + + /* Wait for the worker to end. */ + m_worker->Wait(); + + /* Shutdown the driver. */ + m_driver->Shutdown(); + + /* Notify that we're disconnected. */ + R_ABORT_UNLESS(m_service->NotifyDriverDisconnected()); + } } } diff --git a/libraries/libstratosphere/source/htclow/htclow_packet.hpp b/libraries/libstratosphere/source/htclow/htclow_packet.hpp index 11a53b4bd..681d47321 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet.hpp @@ -20,6 +20,17 @@ namespace ams::htclow { class Packet : public util::IntrusiveListBaseNode { /* TODO */ + public: + virtual ~Packet(); + }; + + struct PacketDeleter { + mem::StandardAllocator *m_allocator; + + void operator()(Packet *packet) { + std::destroy_at(packet); + m_allocator->Free(packet); + } }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp b/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp new file mode 100644 index 000000000..469ab3334 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_packet_factory.hpp" + +namespace ams::htclow { + + void PacketFactory::Delete(Packet *packet) { + PacketDeleter{m_allocator}(packet); + } + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp index 5c0f85dd3..8def47b9f 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include "htclow_packet.hpp" namespace ams::htclow { @@ -23,6 +24,8 @@ namespace ams::htclow { mem::StandardAllocator *m_allocator; public: PacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) { /* ... */ } + + void Delete(Packet *packet); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.cpp b/libraries/libstratosphere/source/htclow/htclow_worker.cpp index bbe6ab99b..714ebee64 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.cpp @@ -18,12 +18,77 @@ namespace ams::htclow { + namespace { + + constexpr inline size_t ThreadStackSize = 4_KB; + + } + Worker::Worker(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv) - : m_thread_stack_size(4_KB), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_driver(nullptr), m_event(os::EventClearMode_ManualClear), m_7C400() + : m_thread_stack_size(ThreadStackSize), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_driver(nullptr), m_event(os::EventClearMode_ManualClear), m_cancelled(false) { /* Allocate stacks. */ m_receive_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment); m_send_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment); } + void Worker::SetDriver(driver::IDriver *driver) { + m_driver = driver; + } + + void Worker::Start() { + /* Clear our cancelled state. */ + m_cancelled = false; + + /* Clear our event. */ + m_event.Clear(); + + /* 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, HtclowReceive))); + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_send_thread), SendThreadEntry, this, m_send_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowSend))); + + /* Set thread names. */ + os::SetThreadNamePointer(std::addressof(m_receive_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowReceive)); + os::SetThreadNamePointer(std::addressof(m_send_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowSend)); + + /* Start our threads. */ + os::StartThread(std::addressof(m_receive_thread)); + os::StartThread(std::addressof(m_send_thread)); + } + + void Worker::Wait() { + 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)); + } + + void Worker::ReceiveThread() { + this->ProcessReceive(); + m_driver->CancelSendReceive(); + this->Cancel(); + } + + void Worker::SendThread() { + this->ProcessSend(); + m_driver->CancelSendReceive(); + this->Cancel(); + } + + void Worker::Cancel() { + /* Set ourselves as cancelled, and signal. */ + m_cancelled = true; + m_event.Signal(); + } + + Result Worker::ProcessReceive() { + /* TODO */ + AMS_ABORT("Worker::ProcessReceive"); + } + + Result Worker::ProcessSend() { + /* TODO */ + AMS_ABORT("Worker::ProcessSend"); + } + } diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.hpp b/libraries/libstratosphere/source/htclow/htclow_worker.hpp index 7c3c9f34f..a30214117 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.hpp @@ -34,9 +34,30 @@ namespace ams::htclow { os::Event m_event; void *m_receive_thread_stack; void *m_send_thread_stack; - u8 m_7C400; + bool m_cancelled; + private: + static void ReceiveThreadEntry(void *arg) { + static_cast(arg)->ReceiveThread(); + } + + static void SendThreadEntry(void *arg) { + static_cast(arg)->SendThread(); + } + + void ReceiveThread(); + void SendThread(); public: Worker(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv); + + void SetDriver(driver::IDriver *driver); + + void Start(); + void Wait(); + private: + void Cancel(); + + Result ProcessReceive(); + Result ProcessSend(); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 474470dbf..1b6d5a43d 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -15,6 +15,7 @@ */ #include #include "htclow_mux.hpp" +#include "../ctrl/htclow_ctrl_state_machine.hpp" namespace ams::htclow::mux { @@ -37,4 +38,28 @@ namespace ams::htclow::mux { } } + void Mux::UpdateChannelState() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Update the state of all channels in our map. */ + /* NOTE: Nintendo does this highly inefficiently... */ + for (auto pair : m_channel_impl_map.GetMap()) { + m_channel_impl_map[pair.first].UpdateState(); + } + } + + void Mux::UpdateMuxState() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Update whether we're sleeping. */ + if (m_state_machine->IsSleeping()) { + m_is_sleeping = true; + } else { + m_is_sleeping = false; + m_wake_event.Signal(); + } + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index 1afb8c2c9..f9e6180a9 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -36,6 +36,9 @@ namespace ams::htclow::mux { Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm); void SetVersion(u16 version); + + void UpdateChannelState(); + void UpdateMuxState(); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp index f10d81a2f..1b8df5361 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -15,6 +15,7 @@ */ #include #include "htclow_mux_channel_impl.hpp" +#include "../ctrl/htclow_ctrl_state_machine.hpp" namespace ams::htclow::mux { @@ -27,4 +28,59 @@ namespace ams::htclow::mux { m_send_buffer.SetVersion(version); } + void ChannelImpl::UpdateState() { + /* Check if shutdown must be forced. */ + if (m_state_machine->IsUnsupportedServiceChannelToShutdown(m_channel)) { + this->ShutdownForce(); + } + + /* Check if we're readied. */ + if (m_state_machine->IsReadied()) { + m_task_manager->NotifyConnectReady(); + } + + /* Update our state transition. */ + if (m_state_machine->IsConnectable(m_channel)) { + if (m_state == ChannelState_Unconnectable) { + this->SetState(ChannelState_Connectable); + } + } else if (m_state_machine->IsUnconnectable()) { + if (m_state == ChannelState_Connectable) { + this->SetState(ChannelState_Unconnectable); + m_state_machine->SetNotConnecting(m_channel); + } else if (m_state == ChannelState_Connected) { + this->ShutdownForce(); + } + } + } + + void ChannelImpl::ShutdownForce() { + /* Clear our send buffer. */ + m_send_buffer.Clear(); + + /* Set our state to shutdown. */ + this->SetState(ChannelState_Shutdown); + } + + void ChannelImpl::SetState(ChannelState state) { + /* Check that we can perform the transition. */ + AMS_ABORT_UNLESS(IsStateTransitionAllowed(m_state, state)); + + /* Perform the transition. */ + this->SetStateWithoutCheck(state); + } + + void ChannelImpl::SetStateWithoutCheck(ChannelState state) { + /* Change our state. */ + if (m_state != state) { + m_state = state; + m_state_change_event.Signal(); + } + + /* If relevant, notify disconnect. */ + if (m_state == ChannelState_Shutdown) { + m_task_manager->NotifyDisconnect(m_channel); + } + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp index e014cd05a..b24a115a8 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -45,12 +45,18 @@ namespace ams::htclow::mux { /* TODO: Channel config */ /* TODO: tracking variables. */ std::optional m_108; - os::Event m_send_packet_event; - /* TODO: Channel state. */ + os::Event m_state_change_event; + ChannelState m_state; public: ChannelImpl(impl::ChannelInternalType channel, PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev); void SetVersion(s16 version); + + void UpdateState(); + private: + void ShutdownForce(); + void SetState(ChannelState state); + void SetStateWithoutCheck(ChannelState state); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp index 2d3693428..0927eba75 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -15,6 +15,7 @@ */ #include #include "htclow_mux_channel_impl.hpp" +#include "../htclow_packet_factory.hpp" namespace ams::htclow::mux { @@ -23,4 +24,12 @@ namespace ams::htclow::mux { m_version = version; } + void SendBuffer::Clear() { + while (!m_packet_list.empty()) { + auto *packet = std::addressof(m_packet_list.front()); + m_packet_list.pop_front(); + m_packet_factory->Delete(packet); + } + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp index cfc5695be..5d7af800f 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp @@ -41,6 +41,8 @@ namespace ams::htclow::mux { SendBuffer(impl::ChannelInternalType channel, PacketFactory *pf); void SetVersion(s16 version); + + void Clear(); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp new file mode 100644 index 000000000..f396a9cd4 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "htclow_mux_task_manager.hpp" + +namespace ams::htclow::mux { + + void TaskManager::NotifyDisconnect(impl::ChannelInternalType channel) { + for (auto i = 0; i < MaxTaskCount; ++i) { + if (m_valid[i] && m_tasks[i].channel == channel) { + this->CompleteTask(i, EventTrigger_Disconnect); + } + } + } + + void TaskManager::NotifyConnectReady() { + for (auto i = 0; i < MaxTaskCount; ++i) { + if (m_valid[i] && m_tasks[i].type == TaskType_Connect) { + this->CompleteTask(i, EventTrigger_ConnectReady); + } + } + } + + void TaskManager::CompleteTask(int index, EventTrigger trigger) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= index && index < MaxTaskCount); + AMS_ASSERT(m_valid[index]); + + /* Complete the task. */ + if (!m_tasks[index].has_event_trigger) { + m_tasks[index].has_event_trigger = true; + m_tasks[index].event_trigger = trigger; + os::SignalEvent(std::addressof(m_tasks[index].event)); + } + } + +} diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp index b0f7d30a8..d82a97dae 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp @@ -20,14 +20,26 @@ namespace ams::htclow::mux { constexpr inline int MaxTaskCount = 0x80; + enum EventTrigger : u8 { + EventTrigger_Disconnect = 1, + EventTrigger_ConnectReady = 11, + }; + class TaskManager { private: + enum TaskType : u8 { + TaskType_Receive = 0, + TaskType_Send = 1, + TaskType_Flush = 6, + TaskType_Connect = 7, + }; + struct Task { impl::ChannelInternalType channel; os::EventType event; - u8 _30; - u8 _31; - u8 _32; + bool has_event_trigger; + EventTrigger event_trigger; + TaskType type; u64 _38; }; private: @@ -35,6 +47,11 @@ namespace ams::htclow::mux { Task m_tasks[MaxTaskCount]; public: TaskManager() : m_valid() { /* ... */ } + + void NotifyDisconnect(impl::ChannelInternalType channel); + void NotifyConnectReady(); + private: + void CompleteTask(int index, EventTrigger trigger); }; } diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp index e78bf0b2b..4e47508c8 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -36,4 +36,6 @@ namespace ams::htclow { R_DEFINE_ERROR_RESULT(UsbDriverReceiveError, 1403); R_DEFINE_ERROR_RESULT(UsbDriverSendError, 1404); + R_DEFINE_ERROR_RESULT(StateTransitionNotAllowed, 2001); + }