diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp index 51601f9c4..cf24af32c 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp @@ -37,6 +37,7 @@ namespace ams::htclow { struct ChannelConfig { bool flow_control_enabled; bool handshake_enabled; + u64 initial_counter_max_data; size_t max_packet_size; }; diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index 724a6f734..c8fc4722c 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -40,7 +40,7 @@ namespace ams::htclow::ctrl { } HtcctrlService::HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux) - : m_settings_holder(), m_beacon_response(), m_1100(), m_packet_factory(pf), m_state_machine(sm), m_mux(mux), m_event(os::EventClearMode_ManualClear), + : m_settings_holder(), m_beacon_response(), m_information_body(), m_packet_factory(pf), m_state_machine(sm), m_mux(mux), m_event(os::EventClearMode_ManualClear), m_send_buffer(pf), m_mutex(), m_condvar(), m_service_channels_packet(), m_version(ProtocolVersion) { /* Lock ourselves. */ @@ -78,6 +78,10 @@ namespace ams::htclow::ctrl { ); } + void HtcctrlService::UpdateInformationBody(const char *status) { + util::SNPrintf(m_information_body, sizeof(m_information_body), "{\r\n \"Status\" : \"%s\"\r\n}\r\n", status); + } + void HtcctrlService::SetDriverType(impl::DriverType driver_type) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -333,6 +337,89 @@ namespace ams::htclow::ctrl { } } + void HtcctrlService::TryReady() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + this->TryReadyInternal(); + } + + void HtcctrlService::Disconnect() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + this->DisconnectInternal(); + } + + void HtcctrlService::Resume() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Send resume packet, if we can. */ + if (const auto state = m_state_machine->GetHtcctrlState(); state == HtcctrlState_Sleep || state == HtcctrlState_ExitSleep) { + /* Send a resume packet. */ + m_send_buffer.AddPacket(m_packet_factory->MakeResumePacket()); + + /* Signal our event. */ + m_event.Signal(); + } + } + + void HtcctrlService::Suspend() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* If we can, perform a suspend. */ + if (m_state_machine->GetHtcctrlState() == HtcctrlState_Ready) { + /* Send a suspend packet. */ + m_send_buffer.AddPacket(m_packet_factory->MakeSuspendPacket()); + + /* Signal our event. */ + m_event.Signal(); + + /* Wait for our state to transition. */ + for (auto state = m_state_machine->GetHtcctrlState(); state == HtcctrlState_Ready || state == HtcctrlState_SentSuspendFromTarget; state = m_state_machine->GetHtcctrlState()) { + m_condvar.Wait(m_mutex); + } + } else { + /* Otherwise, just disconnect. */ + this->DisconnectInternal(); + } + } + + void HtcctrlService::NotifyAwake() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Update our information. */ + this->UpdateInformationBody("Awake"); + + /* Send information to host. */ + this->SendInformation(); + } + + void HtcctrlService::NotifyAsleep() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Update our information. */ + this->UpdateInformationBody("Asleep"); + + /* Send information to host. */ + this->SendInformation(); + } + + void HtcctrlService::SendInformation() { + /* If we need information, send information. */ + if (m_state_machine->IsInformationNeeded()) { + /* Send an information packet. */ + m_send_buffer.AddPacket(m_packet_factory->MakeInformationPacket(m_information_body, util::Strnlen(m_information_body, sizeof(m_information_body)) + 1)); + + /* Signal our event. */ + m_event.Signal(); + } + } + Result HtcctrlService::NotifyDriverConnected() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp index 608d8ada5..3f52ce242 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -42,7 +42,7 @@ namespace ams::htclow::ctrl { private: SettingsHolder m_settings_holder; char m_beacon_response[0x1000]; - u8 m_1100[0x1000]; + char m_information_body[0x1000]; HtcctrlPacketFactory *m_packet_factory; HtcctrlStateMachine *m_state_machine; mux::Mux *m_mux; @@ -56,6 +56,9 @@ namespace ams::htclow::ctrl { const char *GetConnectionType(impl::DriverType driver_type) const; void UpdateBeaconResponse(const char *connection); + void UpdateInformationBody(const char *status); + + void SendInformation(); Result ProcessReceiveConnectPacket(); Result ProcessReceiveReadyPacket(const void *body, size_t body_size); @@ -72,10 +75,12 @@ namespace ams::htclow::ctrl { void ProcessSendDisconnectPacket(); void UpdateServiceChannels(const void *body, size_t body_size); - void TryReadyInternal(); void PrintServiceChannels(char *dst, size_t dst_size); + void TryReadyInternal(); + void DisconnectInternal(); + Result SetState(HtcctrlState state); void ReflectState(); public: @@ -91,6 +96,15 @@ namespace ams::htclow::ctrl { bool QuerySendPacket(HtcctrlPacketHeader *header, HtcctrlPacketBody *body, int *out_body_size); void RemovePacket(const HtcctrlPacketHeader &header); + void TryReady(); + void Disconnect(); + + void Resume(); + void Suspend(); + + void NotifyAwake(); + void NotifyAsleep(); + Result NotifyDriverConnected(); Result NotifyDriverDisconnected(); }; 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 1836dbeeb..e9acb3fd2 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp @@ -57,6 +57,13 @@ namespace ams::htclow::ctrl { return ResultSuccess(); } + bool HtcctrlStateMachine::IsInformationNeeded() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return !ctrl::IsDisconnected(m_state) && m_state != HtcctrlState_DriverConnected; + } + bool HtcctrlStateMachine::IsConnected() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -149,6 +156,16 @@ namespace ams::htclow::ctrl { return ctrl::IsConnected(m_state) && (it == m_map.end() || it->second.connect != ServiceChannelConnect_ConnectingChecked); } + void HtcctrlStateMachine::SetConnecting(const impl::ChannelInternalType &channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + auto it = m_map.find(channel); + if (it != m_map.end() && it->second.connect != ServiceChannelConnect_ConnectingChecked) { + it->second.connect = ServiceChannelConnect_Connecting; + } + } + void HtcctrlStateMachine::SetNotConnecting(const impl::ChannelInternalType &channel) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); 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 aed8d5068..1e9fd254f 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp @@ -58,6 +58,8 @@ namespace ams::htclow::ctrl { bool IsConnectedStatusChanged(); bool IsSleepingStatusChanged(); + bool IsInformationNeeded(); + bool IsConnected(); bool IsReadied(); bool IsUnconnectable(); @@ -68,6 +70,7 @@ namespace ams::htclow::ctrl { bool IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel); bool IsConnectable(const impl::ChannelInternalType &channel); + void SetConnecting(const impl::ChannelInternalType &channel); void SetNotConnecting(const impl::ChannelInternalType &channel); void SetConnectingChecked(); diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp index 8f951c9db..2f7220387 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp @@ -55,6 +55,20 @@ namespace ams::htclow::driver { return ResultSuccess(); } + void DriverManager::CloseDriver() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Clear our driver type. */ + m_driver_type = std::nullopt; + + /* Close our driver. */ + if (m_open_driver != nullptr) { + m_open_driver->Close(); + m_open_driver = nullptr; + } + } + impl::DriverType DriverManager::GetDriverType() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -69,6 +83,10 @@ namespace ams::htclow::driver { return m_open_driver; } + void DriverManager::Cancel() { + m_open_driver->CancelSendReceive(); + } + void DriverManager::SetDebugDriver(IDriver *driver) { m_debug_driver = driver; m_driver_type = impl::DriverType::Debug; diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp index 1b0d3d967..f19d46d1e 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp @@ -33,11 +33,14 @@ namespace ams::htclow::driver { DriverManager() = default; Result OpenDriver(impl::DriverType driver_type); + void CloseDriver(); impl::DriverType GetDriverType(); IDriver *GetCurrentDriver(); + void Cancel(); + void SetDebugDriver(IDriver *driver); }; diff --git a/libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp b/libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp index a442ef2ed..27d58a82e 100644 --- a/libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_default_channel_config.hpp @@ -20,9 +20,10 @@ namespace ams::htclow { constexpr inline const ChannelConfig DefaultChannelConfig = { - .flow_control_enabled = true, - .handshake_enabled = true, - .max_packet_size = 0xE000 + sizeof(PacketHeader), + .flow_control_enabled = true, + .handshake_enabled = true, + .initial_counter_max_data = 0, + .max_packet_size = 0xE000 + sizeof(PacketHeader), }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.cpp b/libraries/libstratosphere/source/htclow/htclow_listener.cpp index 278cfc7cb..a7dd94bea 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.cpp @@ -55,6 +55,34 @@ namespace ams::htclow { os::StartThread(std::addressof(m_listen_thread)); } + void Listener::Cancel() { + /* Mark ourselves as cancelled. */ + m_cancelled = true; + + /* Cancel our worker. */ + m_worker->Cancel(); + + /* Signal our event. */ + m_event.Signal(); + + /* Cancel our driver. */ + m_driver->CancelSendReceive(); + } + + void Listener::Wait() { + /* Wait for our listen thread to exit. */ + os::WaitThread(std::addressof(m_listen_thread)); + + /* Destroy our listen thread. */ + os::DestroyThread(std::addressof(m_listen_thread)); + + /* Clear our driver. */ + m_driver = nullptr; + + /* Mark our thread as not running. */ + m_thread_running = false; + } + void Listener::ListenThread() { /* Check pre-conditions. */ AMS_ASSERT(m_driver != nullptr); diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.hpp b/libraries/libstratosphere/source/htclow/htclow_listener.hpp index 17e4aa698..a2c1fdbb6 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.hpp @@ -42,6 +42,8 @@ namespace ams::htclow { Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker); void Start(driver::IDriver *driver); + void Cancel(); + void Wait(); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.hpp b/libraries/libstratosphere/source/htclow/htclow_worker.hpp index 6efb0d6c9..34a3ca8aa 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.hpp @@ -56,10 +56,9 @@ namespace ams::htclow { void SetDriver(driver::IDriver *driver); void Start(); + void Cancel(); 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 656ce104f..2419e54b9 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -182,6 +182,58 @@ namespace ams::htclow::mux { return ResultSuccess(); } + Result Mux::Close(impl::ChannelInternalType channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* If we have the channel, close it. */ + if (auto it = m_channel_impl_map.GetMap().find(channel); it != m_channel_impl_map.GetMap().end()) { + /* Shut down the channel. */ + m_channel_impl_map[it->second].ShutdownForce(); + + /* Remove the channel. */ + R_ABORT_UNLESS(m_channel_impl_map.RemoveChannel(channel)); + } + + return ResultSuccess(); + } + + Result Mux::ConnectBegin(u32 *out_task_id, impl::ChannelInternalType channel) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist()); + + /* Perform the connection. */ + return m_channel_impl_map[it->second].DoConnectBegin(out_task_id); + } + + Result Mux::ConnectEnd(impl::ChannelInternalType channel, u32 task_id) { + /* Get the trigger for the task. */ + const auto trigger = m_task_manager.GetTrigger(task_id); + + /* Free the task. */ + m_task_manager.FreeTask(task_id); + + /* Check that we didn't hit a disconnect. */ + R_UNLESS(trigger != EventTrigger_Disconnect, htclow::ResultInvalidChannelStateDisconnected()); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist()); + + /* Perform the disconnection. */ + return m_channel_impl_map[it->second].DoConnectEnd(); + } + + ChannelState Mux::GetChannelState(impl::ChannelInternalType channel); + os::EventType *Mux::GetChannelStateEvent(impl::ChannelInternalType channel); + + Result Mux::FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel); + Result Mux::FlushEnd(u32 task_id); + os::EventType *Mux::GetTaskEvent(u32 task_id) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -189,6 +241,12 @@ namespace ams::htclow::mux { return m_task_manager.GetTaskEvent(task_id); } + Result Mux::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking); + Result Mux::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id); + + Result Mux::SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); + Result Mux::SendEnd(u32 task_id); + void Mux::SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size, size_t max_packet_size) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -225,4 +283,6 @@ namespace ams::htclow::mux { m_channel_impl_map[it->second].SetReceiveBuffer(buf, buf_size); } + Result Mux::Shutdown(impl::ChannelInternalType channel); + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index 39d900aad..b8c005e10 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -55,12 +55,30 @@ namespace ams::htclow::mux { void UpdateMuxState(); public: Result Open(impl::ChannelInternalType channel); + Result Close(impl::ChannelInternalType channel); + + Result ConnectBegin(u32 *out_task_id, impl::ChannelInternalType channel); + Result ConnectEnd(impl::ChannelInternalType channel, u32 task_id); + + ChannelState GetChannelState(impl::ChannelInternalType channel); + os::EventType *GetChannelStateEvent(impl::ChannelInternalType channel); + + Result FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel); + Result FlushEnd(u32 task_id); os::EventType *GetTaskEvent(u32 task_id); + Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking); + Result ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id); + + Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); + Result SendEnd(u32 task_id); + void SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size, size_t max_packet_size); void SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size); void SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size, size_t max_packet_size); + + Result Shutdown(impl::ChannelInternalType channel); private: Result CheckChannelExist(impl::ChannelInternalType channel); 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 c683aaa76..7d173e1b1 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -17,6 +17,7 @@ #include "htclow_mux_channel_impl.hpp" #include "../ctrl/htclow_ctrl_state_machine.hpp" #include "../htclow_default_channel_config.hpp" +#include "../htclow_packet_factory.hpp" namespace ams::htclow::mux { @@ -228,6 +229,67 @@ namespace ams::htclow::mux { } } + Result ChannelImpl::DoConnectBegin(u32 *out_task_id) { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connectable})); + + /* Set ourselves as connecting. */ + m_state_machine->SetConnecting(m_channel); + + /* Allocate a task. */ + u32 task_id; + R_TRY(m_task_manager->AllocateTask(std::addressof(task_id), m_channel)); + + /* Configure the task. */ + m_task_manager->ConfigureConnectTask(task_id); + + /* If we're ready, complete the task immediately. */ + if (m_state_machine->IsReadied()) { + m_task_manager->CompleteTask(task_id, EventTrigger_ConnectReady); + } + + /* Set the output task id. */ + *out_task_id = task_id; + return ResultSuccess(); + } + + Result ChannelImpl::DoConnectEnd() { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connectable})); + + /* Perform handshake, if we should. */ + if (m_config.handshake_enabled) { + /* Set our next max data. */ + m_next_max_data = m_receive_buffer.GetBufferSize(); + + /* Make a max data packet. */ + auto packet = m_packet_factory->MakeMaxDataPacket(m_channel, m_version, m_next_max_data); + R_UNLESS(packet, htclow::ResultOutOfMemory()); + + /* Send the packet. */ + m_send_buffer.AddPacket(std::move(packet)); + + /* Signal that we have an packet to send. */ + this->SignalSendPacketEvent(); + + /* Set our current max data. */ + m_cur_max_data = m_next_max_data; + } else { + /* Set our share. */ + m_share = m_config.initial_counter_max_data; + + /* If we're not empty, signal. */ + if (!m_send_buffer.Empty()) { + this->SignalSendPacketEvent(); + } + } + + /* Set our state as connected. */ + this->SetState(ChannelState_Connected); + + return ResultSuccess(); + } + void ChannelImpl::SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size) { /* Set buffer. */ m_send_buffer.SetBuffer(buf, buf_size); 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 27191d2e1..cba3e28b7 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -61,13 +61,17 @@ namespace ams::htclow::mux { void RemovePacket(const PacketHeader &header); + void ShutdownForce(); + void UpdateState(); public: + Result DoConnectBegin(u32 *out_task_id); + Result DoConnectEnd(); + void SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size); void SetReceiveBuffer(void *buf, size_t buf_size); void SetSendBufferWithData(const void *buf, size_t buf_size, size_t max_packet_size); private: - void ShutdownForce(); void SetState(ChannelState state); void SetStateWithoutCheck(ChannelState state); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp index 9ed955bd6..8cc55f7c3 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.cpp @@ -53,7 +53,7 @@ namespace ams::htclow::mux { } /* Validate that the storage is free. */ - R_UNLESS(idx < MaxChannelCount, htclow::ResultOutOfResource()); + R_UNLESS(idx < MaxChannelCount, htclow::ResultOutOfChannel()); /* Create the channel impl. */ std::construct_at(GetPointer(m_channel_storage[idx]), channel, m_packet_factory, m_state_machine, m_task_manager, m_event); @@ -67,4 +67,28 @@ namespace ams::htclow::mux { return ResultSuccess(); } + Result ChannelImplMap::RemoveChannel(impl::ChannelInternalType channel) { + /* Find the storage. */ + auto it = m_map.find(channel); + AMS_ASSERT(it != m_map.end()); + + /* Get the channel index. */ + const auto index = it->second; + AMS_ASSERT(0 <= index && index < MaxChannelCount); + + /* Get the channel impl. */ + auto *channel_impl = GetPointer(m_channel_storage[index]); + + /* Mark the storage as invalid. */ + m_storage_valid[index] = false; + + /* Erase the channel from the map. */ + m_map.erase(channel); + + /* Destroy the channel. */ + std::destroy_at(channel_impl); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp index a949b8261..dee89de16 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl_map.hpp @@ -48,6 +48,7 @@ namespace ams::htclow::mux { } Result AddChannel(impl::ChannelInternalType channel); + Result RemoveChannel(impl::ChannelInternalType channel); private: public: MapType &GetMap() { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp index e27f36b60..0dfbef6c3 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp @@ -41,6 +41,12 @@ namespace ams::htclow::mux { m_is_read_only = true; } + void RingBuffer::Clear() { + m_data_size = 0; + m_offset = 0; + m_can_discard = false; + } + Result RingBuffer::Write(const void *data, size_t size) { /* Validate pre-conditions. */ AMS_ASSERT(!m_is_read_only); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp index 1ed9c8d38..7e04b89cb 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp @@ -33,6 +33,9 @@ namespace ams::htclow::mux { void Initialize(void *buffer, size_t buffer_size); void InitializeForReadOnly(const void *buffer, size_t buffer_size); + void Clear(); + + size_t GetBufferSize() { return m_buffer_size; } size_t GetDataSize() { return m_data_size; } Result Write(const void *data, size_t size); 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 b7961a8ba..2d52214b2 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -19,6 +19,11 @@ namespace ams::htclow::mux { + SendBuffer::~SendBuffer() { + m_ring_buffer.Clear(); + this->Clear(); + } + bool SendBuffer::IsPriorPacket(PacketType packet_type) const { return packet_type == PacketType_MaxData; } @@ -94,6 +99,17 @@ namespace ams::htclow::mux { return true; } + void SendBuffer::AddPacket(std::unique_ptr ptr) { + /* Get the packet. */ + auto *packet = ptr.release(); + + /* Check the packet type. */ + AMS_ABORT_UNLESS(this->IsPriorPacket(packet->GetHeader()->packet_type)); + + /* Add the packet. */ + m_packet_list.push_back(*packet); + } + void SendBuffer::RemovePacket(const PacketHeader &header) { /* Get the packet type. */ const auto packet_type = header.packet_type; 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 65aa19fe5..f35dea9fd 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp @@ -45,11 +45,13 @@ namespace ams::htclow::mux { void CopyPacket(PacketHeader *header, PacketBody *body, int *out_body_size, const Packet &packet); public: SendBuffer(impl::ChannelInternalType channel, PacketFactory *pf); + ~SendBuffer(); void SetVersion(s16 version); bool QueryNextPacket(PacketHeader *header, PacketBody *body, int *out_body_size, u64 max_data, u64 total_send_size, bool has_share, u64 share); + void AddPacket(std::unique_ptr ptr); void RemovePacket(const PacketHeader &header); void SetBuffer(void *buffer, size_t buffer_size); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp index ce5f67576..2f5b67698 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp @@ -26,6 +26,89 @@ namespace ams::htclow::mux { return std::addressof(m_tasks[task_id].event); } + EventTrigger TaskManager::GetTrigger(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + AMS_ASSERT(m_valid[task_id]); + + return m_tasks[task_id].event_trigger; + } + + Result TaskManager::AllocateTask(u32 *out_task_id, impl::ChannelInternalType channel) { + /* Find a free task. */ + u32 task_id = 0; + for (task_id = 0; task_id < util::size(m_tasks); ++task_id) { + if (!m_valid[task_id]) { + break; + } + } + + /* Verify the task is free. */ + R_UNLESS(!m_valid[task_id], htclow::ResultOutOfTask()); + + /* Mark the task as allocated. */ + m_valid[task_id] = true; + + /* Setup the task. */ + m_tasks[task_id].channel = channel; + m_tasks[task_id].has_event_trigger = false; + os::InitializeEvent(std::addressof(m_tasks[task_id].event), false, os::EventClearMode_ManualClear); + + /* Return the task id. */ + *out_task_id = task_id; + return ResultSuccess(); + } + + void TaskManager::FreeTask(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + + /* Invalidate the task. */ + if (m_valid[task_id]) { + os::FinalizeEvent(std::addressof(m_tasks[task_id].event)); + m_valid[task_id] = false; + } + } + + void TaskManager::ConfigureConnectTask(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + AMS_ASSERT(m_valid[task_id]); + + /* Set the task type. */ + m_tasks[task_id].type = TaskType_Connect; + } + + void TaskManager::ConfigureFlushTask(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + AMS_ASSERT(m_valid[task_id]); + + /* Set the task type. */ + m_tasks[task_id].type = TaskType_Flush; + } + + void TaskManager::ConfigureReceiveTask(u32 task_id, size_t size) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + AMS_ASSERT(m_valid[task_id]); + + /* Set the task type. */ + m_tasks[task_id].type = TaskType_Receive; + + /* Set the task size. */ + m_tasks[task_id].size = size; + } + + void TaskManager::ConfigureSendTask(u32 task_id) { + /* Check pre-conditions. */ + AMS_ASSERT(0 <= task_id && task_id < MaxTaskCount); + AMS_ASSERT(m_valid[task_id]); + + /* Set the task type. */ + m_tasks[task_id].type = TaskType_Send; + } + void TaskManager::NotifyDisconnect(impl::ChannelInternalType channel) { for (auto i = 0; i < MaxTaskCount; ++i) { if (m_valid[i] && m_tasks[i].channel == channel) { 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 bb27d3182..69f9ae779 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp @@ -51,14 +51,23 @@ namespace ams::htclow::mux { public: TaskManager() : m_valid() { /* ... */ } + Result AllocateTask(u32 *out_task_id, impl::ChannelInternalType channel); + void FreeTask(u32 task_id); + os::EventType *GetTaskEvent(u32 task_id); + EventTrigger GetTrigger(u32 task_id); + + void ConfigureConnectTask(u32 task_id); + void ConfigureFlushTask(u32 task_id); + void ConfigureReceiveTask(u32 task_id, size_t size); + void ConfigureSendTask(u32 task_id); void NotifyDisconnect(impl::ChannelInternalType channel); void NotifyReceiveData(impl::ChannelInternalType channel, size_t size); void NotifySendReady(); void NotifySendBufferEmpty(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 ef8976ff8..16e65a413 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -26,7 +26,8 @@ namespace ams::htclow { R_DEFINE_ERROR_RESULT(ChannelAlreadyExist, 9); R_DEFINE_ERROR_RESULT(ChannelNotExist, 10); - R_DEFINE_ERROR_RESULT(OutOfResource, 151); + R_DEFINE_ERROR_RESULT(OutOfChannel, 151); + R_DEFINE_ERROR_RESULT(OutOfTask, 151); R_DEFINE_ERROR_RESULT(InvalidChannelState, 200); R_DEFINE_ERROR_RESULT(InvalidChannelStateDisconnected, 201);