diff --git a/libraries/libstratosphere/source/htclow/htclow_channel.cpp b/libraries/libstratosphere/source/htclow/htclow_channel.cpp new file mode 100644 index 000000000..830369e25 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_channel.cpp @@ -0,0 +1,235 @@ +/* + * 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_channel.hpp" + +namespace ams::htclow { + + Result Channel::Open(const Module *module, ChannelId id) { + /* Check we're not already initialized. */ + AMS_ASSERT(!module->GetBase()->_is_initialized); + + /* Set our channel type. */ + m_channel = { + ._is_initialized = true, + ._module_id = module->GetBase()->_id, + ._channel_id = id, + }; + + /* Open the channel. */ + return m_manager->Open(impl::ConvertChannelType(m_channel)); + } + + void Channel::Close() { + m_manager->Close(impl::ConvertChannelType(m_channel)); + } + + ChannelState Channel::GetChannelState() { + return m_manager->GetChannelState(impl::ConvertChannelType(m_channel)); + } + + os::EventType *Channel::GetChannelStateEvent() { + return m_manager->GetChannelStateEvent(impl::ConvertChannelType(m_channel)); + } + + Result Channel::Connect() { + const auto channel = impl::ConvertChannelType(m_channel); + + /* Begin the flush. */ + u32 task_id; + R_TRY(m_manager->ConnectBegin(std::addressof(task_id), channel)); + + /* Wait for the task to finish. */ + this->WaitEvent(m_manager->GetTaskEvent(task_id), false); + + /* End the flush. */ + return m_manager->ConnectEnd(channel, task_id); + } + + Result Channel::Flush() { + /* Begin the flush. */ + u32 task_id; + R_TRY(m_manager->FlushBegin(std::addressof(task_id), impl::ConvertChannelType(m_channel))); + + /* Wait for the task to finish. */ + this->WaitEvent(m_manager->GetTaskEvent(task_id), true); + + /* End the flush. */ + return m_manager->FlushEnd(task_id); + } + + void Channel::Shutdown() { + m_manager->Shutdown(impl::ConvertChannelType(m_channel)); + } + + Result Channel::Receive(s64 *out, void *dst, s64 size, ReceiveOption option) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(util::IsIntValueRepresentable(size)); + + /* Determine the minimum allowable receive size. */ + s64 min_size; + switch (option) { + case htclow::ReceiveOption_NonBlocking: min_size = 0; break; + case htclow::ReceiveOption_ReceiveAnyData: min_size = 1; break; + case htclow::ReceiveOption_ReceiveAllData: min_size = size; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Repeatedly receive. */ + s64 received = 0; + do { + s64 cur_received; + const Result result = this->ReceiveInternal(std::addressof(cur_received), static_cast(dst) + received, size - received, option); + + if (R_FAILED(result)) { + if (htclow::ResultChannelReceiveBufferEmpty::Includes(result)) { + R_UNLESS(option != htclow::ReceiveOption_NonBlocking, htclow::ResultNonBlockingReceiveFailed()); + } + if (htclow::ResultChannelNotExist::Includes(result)) { + *out = received; + } + return result; + } + + received += static_cast(cur_received); + } while (received < min_size); + + /* Set output size. */ + AMS_ASSERT(received <= size); + *out = received; + + return ResultSuccess(); + } + + Result Channel::Send(s64 *out, const void *src, s64 size, ReceiveOption option) { + /* Check pre-conditions. */ + AMS_ASSERT(util::IsIntValueRepresentable(size)); + + /* Convert channel. */ + const auto channel = impl::ConvertChannelType(m_channel); + + /* Loop sending. */ + s64 total_sent; + size_t cur_sent; + for (total_sent = 0; total_sent < size; total_sent += cur_sent) { + AMS_ASSERT(util::IsIntValueRepresentable(size - total_sent)); + + /* Begin the send. */ + u32 task_id; + const auto begin_result = m_manager->SendBegin(std::addressof(task_id), std::addressof(cur_sent), static_cast(src) + total_sent, size - total_sent, channel); + if (R_FAILED(begin_result)) { + if (total_sent != 0) { + break; + } else { + return begin_result; + } + } + + /* Wait for the task to finish. */ + this->WaitEvent(m_manager->GetTaskEvent(task_id), true); + + /* Finish the send. */ + R_ABORT_UNLESS(m_manager->SendEnd(task_id)); + } + + /* Set output size. */ + AMS_ASSERT(total_sent <= size); + *out = total_sent; + + return ResultSuccess(); + } + + void Channel::SetConfig(const ChannelConfig &config) { + m_manager->SetConfig(impl::ConvertChannelType(m_channel), config); + } + + void Channel::SetReceiveBuffer(void *buf, size_t size) { + m_manager->SetReceiveBuffer(impl::ConvertChannelType(m_channel), buf, size); + } + + void Channel::SetSendBuffer(void *buf, size_t size) { + m_manager->SetSendBuffer(impl::ConvertChannelType(m_channel), buf, size); + } + + void Channel::SetSendBufferWithData(const void *buf, size_t size) { + m_manager->SetSendBufferWithData(impl::ConvertChannelType(m_channel), buf, size); + } + + Result Channel::WaitReceive(s64 size) { + /* Check pre-conditions. */ + AMS_ASSERT(util::IsIntValueRepresentable(size)); + + return this->WaitReceiveInternal(size, nullptr); + } + + Result Channel::WaitReceive(s64 size, os::EventType *event) { + /* Check pre-conditions. */ + AMS_ASSERT(util::IsIntValueRepresentable(size)); + AMS_ASSERT(event != nullptr); + + return this->WaitReceiveInternal(size, event); + } + + void Channel::WaitEvent(os::EventType *event, bool) { + return os::WaitEvent(event); + } + + Result Channel::ReceiveInternal(s64 *out, void *dst, s64 size, ReceiveOption option) { + const auto channel = impl::ConvertChannelType(m_channel); + const bool blocking = option != ReceiveOption_NonBlocking; + + /* Begin the receive. */ + u32 task_id; + R_TRY(m_manager->ReceiveBegin(std::addressof(task_id), channel, blocking ? 1 : 0)); + + /* Wait for the task to finish. */ + this->WaitEvent(m_manager->GetTaskEvent(task_id), false); + + /* Receive the data. */ + size_t received; + AMS_ASSERT(util::IsIntValueRepresentable(size)); + R_TRY(m_manager->ReceiveEnd(std::addressof(received), dst, static_cast(size), channel, task_id)); + + /* Set the output size. */ + AMS_ASSERT(util::IsIntValueRepresentable(received)); + *out = static_cast(received); + + return ResultSuccess(); + } + + Result Channel::WaitReceiveInternal(s64 size, os::EventType *event) { + const auto channel = impl::ConvertChannelType(m_channel); + + /* Begin the wait. */ + u32 task_id; + R_TRY(m_manager->WaitReceiveBegin(std::addressof(task_id), channel, size)); + + + /* Perform the wait. */ + if (event != nullptr) { + if (os::WaitAny(event, m_manager->GetTaskEvent(task_id)) == 0) { + m_manager->WaitReceiveEnd(task_id); + return htclow::ResultChannelWaitCancelled(); + } + } else { + this->WaitEvent(m_manager->GetTaskEvent(task_id), false); + } + + /* End the wait. */ + return m_manager->WaitReceiveEnd(task_id); + } + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_channel.hpp b/libraries/libstratosphere/source/htclow/htclow_channel.hpp new file mode 100644 index 000000000..2d99cc7c4 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/htclow_channel.hpp @@ -0,0 +1,58 @@ +/* + * 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 . + */ +#pragma once +#include +#include "htclow_manager.hpp" + +namespace ams::htclow { + + class Channel final { + private: + HtclowManager *m_manager; + ChannelType m_channel; + public: + constexpr Channel(HtclowManager *manager) : m_manager(manager), m_channel() { /* ... */ } + constexpr ~Channel() { m_channel = {}; } + + ChannelType *GetBase() { return std::addressof(m_channel); } + public: + Result Open(const Module *module, ChannelId id); + void Close(); + + ChannelState GetChannelState(); + os::EventType *GetChannelStateEvent(); + + Result Connect(); + Result Flush(); + void Shutdown(); + + Result Receive(s64 *out, void *dst, s64 size, ReceiveOption option); + Result Send(s64 *out, const void *src, s64 size, ReceiveOption option); + + void SetConfig(const ChannelConfig &config); + void SetReceiveBuffer(void *buf, size_t size); + void SetSendBuffer(void *buf, size_t size); + void SetSendBufferWithData(const void *buf, size_t size); + + Result WaitReceive(s64 size); + Result WaitReceive(s64 size, os::EventType *event); + private: + void WaitEvent(os::EventType *event, bool); + Result ReceiveInternal(s64 *out, void *dst, s64 size, ReceiveOption option); + Result WaitReceiveInternal(s64 size, os::EventType *event); + }; + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_manager.cpp b/libraries/libstratosphere/source/htclow/htclow_manager.cpp index 398de260a..66990a084 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager.cpp @@ -96,8 +96,8 @@ namespace ams::htclow { return m_impl->NotifyAwake(); } - Result HtclowManager::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking) { - return m_impl->ReceiveBegin(out_task_id, channel, blocking); + Result HtclowManager::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) { + return m_impl->ReceiveBegin(out_task_id, channel, size); } Result HtclowManager::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id) { @@ -112,6 +112,14 @@ namespace ams::htclow { return m_impl->SendEnd(task_id); } + Result HtclowManager::WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) { + return m_impl->WaitReceiveBegin(out_task_id, channel, size); + } + + Result HtclowManager::WaitReceiveEnd(u32 task_id) { + return m_impl->WaitReceiveEnd(task_id); + } + void HtclowManager::SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config) { return m_impl->SetConfig(channel, config); } diff --git a/libraries/libstratosphere/source/htclow/htclow_manager.hpp b/libraries/libstratosphere/source/htclow/htclow_manager.hpp index 1842d2e5c..d01cc891a 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager.hpp @@ -56,12 +56,15 @@ namespace ams::htclow { void NotifyAsleep(); void NotifyAwake(); - Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, bool blocking); + Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size); 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); + Result WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size); + Result WaitReceiveEnd(u32 task_id); + void SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config); void SetDebugDriver(driver::IDriver *driver); diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp index 7e9c61b7e..d4f6d2e8a 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -173,8 +173,16 @@ namespace ams::htclow { return m_mux.SendEnd(task_id); } + Result HtclowManagerImpl::WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) { + return m_mux.WaitReceiveBegin(out_task_id, channel, size); + } + + Result HtclowManagerImpl::WaitReceiveEnd(u32 task_id) { + return m_mux.WaitReceiveEnd(task_id); + } + void HtclowManagerImpl::SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config) { - AMS_ABORT("HtclowManagerImpl::SetConfig"); + return m_mux.SetConfig(channel, config); } void HtclowManagerImpl::SetDebugDriver(driver::IDriver *driver) { diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp index 6839eab98..ce7eb0aea 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.hpp @@ -74,6 +74,9 @@ namespace ams::htclow { Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); Result SendEnd(u32 task_id); + Result WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size); + Result WaitReceiveEnd(u32 task_id); + void SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config); void SetDebugDriver(driver::IDriver *driver); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 3bb6b9d19..220d28c62 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -211,6 +211,9 @@ namespace ams::htclow::mux { } Result Mux::ConnectEnd(impl::ChannelInternalType channel, u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + /* Get the trigger for the task. */ const auto trigger = m_task_manager.GetTrigger(task_id); @@ -255,6 +258,9 @@ namespace ams::htclow::mux { } Result Mux::FlushEnd(u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + /* Get the trigger for the task. */ const auto trigger = m_task_manager.GetTrigger(task_id); @@ -287,6 +293,9 @@ namespace ams::htclow::mux { } Result Mux::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + /* Free the task. */ m_task_manager.FreeTask(task_id); @@ -317,6 +326,9 @@ namespace ams::htclow::mux { } Result Mux::SendEnd(u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + /* Get the trigger for the task. */ const auto trigger = m_task_manager.GetTrigger(task_id); @@ -329,6 +341,38 @@ namespace ams::htclow::mux { return ResultSuccess(); } + Result Mux::WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) { + return this->ReceiveBegin(out_task_id, channel, size); + } + + Result Mux::WaitReceiveEnd(u32 task_id) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* 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()); + + return ResultSuccess(); + } + + void Mux::SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Find the channel. */ + auto it = m_channel_impl_map.GetMap().find(channel); + AMS_ABORT_UNLESS(it != m_channel_impl_map.GetMap().end()); + + /* Perform the connection. */ + return m_channel_impl_map[it->second].SetConfig(config); + } + 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); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index 071efbe17..c3686223d 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -74,6 +74,11 @@ namespace ams::htclow::mux { Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel); Result SendEnd(u32 task_id); + Result WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size); + Result WaitReceiveEnd(u32 task_id); + + void SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config); + 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); 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 4b8aa3a9a..b465eeadf 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -425,6 +425,17 @@ namespace ams::htclow::mux { return ResultSuccess(); } + void ChannelImpl::SetConfig(const ChannelConfig &config) { + /* Check our state. */ + R_ABORT_UNLESS(this->CheckState({ChannelState_Unconnectable, ChannelState_Connectable})); + + /* Set our config. */ + m_config = config; + + /* Set flow control for our send buffer. */ + m_send_buffer.SetFlowControlEnabled(m_config.flow_control_enabled); + } + 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 c476aab77..5849b88a5 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -80,6 +80,8 @@ namespace ams::htclow::mux { Result DoShutdown(); + void SetConfig(const ChannelConfig &config); + 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); 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 3250bbbbb..214bc3032 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -41,6 +41,11 @@ namespace ams::htclow::mux { m_version = version; } + void SendBuffer::SetFlowControlEnabled(bool en) { + /* Set flow control enabled. */ + m_flow_control_enabled = en; + } + void SendBuffer::MakeDataPacketHeader(PacketHeader *header, int body_size, s16 version, u64 share, u32 offset) const { /* Set all packet fields. */ header->signature = HtcGen2Signature; 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 665443459..373cece1b 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.hpp @@ -48,6 +48,7 @@ namespace ams::htclow::mux { ~SendBuffer(); void SetVersion(s16 version); + void SetFlowControlEnabled(bool en); bool QueryNextPacket(PacketHeader *header, PacketBody *body, int *out_body_size, u64 max_data, u64 total_send_size, bool has_share, u64 share); diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp index 16e65a413..eb90e2b90 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -23,6 +23,7 @@ namespace ams::htclow { R_DEFINE_ERROR_RESULT(ConnectionFailure, 1); R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); R_DEFINE_ERROR_RESULT(NonBlockingReceiveFailed, 5); + R_DEFINE_ERROR_RESULT(ChannelWaitCancelled, 8); R_DEFINE_ERROR_RESULT(ChannelAlreadyExist, 9); R_DEFINE_ERROR_RESULT(ChannelNotExist, 10);