diff --git a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp index b1658127c..685f78372 100644 --- a/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/htclow/htclow_channel_types.hpp @@ -34,6 +34,10 @@ namespace ams::htclow { ChannelState_Disconnected = 3, }; + struct ChannelConfig { + bool flow_control_enabled; + }; + constexpr bool IsStateTransitionAllowed(ChannelState from, ChannelState to) { switch (from) { case ChannelState_Connectable: diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp index 574dcf845..bb12eb3dd 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp @@ -15,11 +15,59 @@ */ #pragma once #include +#include "../htclow_packet.hpp" namespace ams::htclow::ctrl { - class HtcctrlPacket : public util::IntrusiveListBaseNode { - /* TODO */ + enum HtcctrlPacketType : u16 { + HtcctrlPacketType_ConnectFromHost = 16, + HtcctrlPacketType_ConnectFromTarget = 17, + HtcctrlPacketType_ReadyFromHost = 18, + HtcctrlPacketType_ReadyFromTarget = 19, + HtcctrlPacketType_SuspendFromHost = 20, + HtcctrlPacketType_SuspendFromTarget = 21, + HtcctrlPacketType_ResumeFromHost = 22, + HtcctrlPacketType_ResumeFromTarget = 23, + HtcctrlPacketType_DisconnectFromHost = 24, + HtcctrlPacketType_DisconnectFromTarget = 25, + HtcctrlPacketType_BeaconQuery = 28, + HtcctrlPacketType_BeaconResponse = 29, + HtcctrlPacketType_InformationFromTarget = 33, + }; + + static constexpr inline u32 HtcctrlSignature = 0x78825637; + + struct HtcctrlPacketHeader { + u32 signature; + u32 offset; + u32 reserved; + u32 body_size; + s16 version; + HtcctrlPacketType packet_type; + impl::ChannelInternalType channel; + u64 share; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(HtcctrlPacketHeader) == 0x20); + + static constexpr inline size_t HtcctrlPacketBodySizeMax = 0x1000; + + struct HtcctrlPacketBody { + u8 data[HtcctrlPacketBodySizeMax]; + }; + + class HtcctrlPacket : public BasePacket, public util::IntrusiveListBaseNode { + public: + using BasePacket::BasePacket; + }; + + struct HtcctrlPacketDeleter { + mem::StandardAllocator *m_allocator; + + void operator()(HtcctrlPacket *packet) { + std::destroy_at(packet); + m_allocator->Free(packet); + } }; } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index 9c5e43323..55bb00d02 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -83,6 +83,37 @@ namespace ams::htclow::ctrl { this->UpdateBeaconResponse(this->GetConnectionType(driver_type)); } + Result HtcctrlService::CheckReceivedHeader(const HtcctrlPacketHeader &header) const { + /* Check the packet signature. */ + AMS_ASSERT(header.signature == HtcctrlSignature); + + /* Validate version. */ + R_UNLESS(header.version == 1, htclow::ResultProtocolError()); + + /* Switch on the packet type. */ + switch (header.packet_type) { + case HtcctrlPacketType_ConnectFromHost: + case HtcctrlPacketType_SuspendFromHost: + case HtcctrlPacketType_ResumeFromHost: + case HtcctrlPacketType_DisconnectFromHost: + case HtcctrlPacketType_BeaconQuery: + R_UNLESS(header.body_size == 0, htclow::ResultProtocolError()); + break; + case HtcctrlPacketType_ReadyFromHost: + R_UNLESS(0 <= header.body_size && header.body_size <= sizeof(HtcctrlPacketBody), htclow::ResultProtocolError()); + break; + default: + return htclow::ResultProtocolError(); + } + + return ResultSuccess(); + } + + Result HtcctrlService::ProcessReceivePacket(const HtcctrlPacketHeader &header, const void *body, size_t body_size) { + /* TODO */ + AMS_ABORT("HtcctrlService::ProcessReceivePacket"); + } + 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 5d83d5698..b39fb6f41 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -64,6 +64,9 @@ namespace ams::htclow::ctrl { void SetDriverType(impl::DriverType driver_type); + Result CheckReceivedHeader(const HtcctrlPacketHeader &header) const; + Result ProcessReceivePacket(const HtcctrlPacketHeader &header, const void *body, size_t body_size); + Result NotifyDriverConnected(); Result NotifyDriverDisconnected(); }; diff --git a/libraries/libstratosphere/source/htclow/htclow_packet.hpp b/libraries/libstratosphere/source/htclow/htclow_packet.hpp index 681d47321..369753d1d 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet.hpp @@ -18,10 +18,86 @@ namespace ams::htclow { - class Packet : public util::IntrusiveListBaseNode { - /* TODO */ + enum PacketType : u16 { + PacketType_Data = 24, + PacketType_MaxData = 25, + PacketType_Error = 26, + }; + + static constexpr inline u32 HtcGen2Signature = 0xA79F3540; + + struct PacketHeader { + u32 signature; + u32 offset; + u32 reserved; + u32 body_size; + s16 version; + PacketType packet_type; + impl::ChannelInternalType channel; + u64 share; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(PacketHeader) == 0x20); + + static constexpr inline size_t PacketBodySizeMax = 0x3E000; + + struct PacketBody { + u8 data[PacketBodySizeMax]; + }; + + template + class BasePacket { + private: + mem::StandardAllocator *m_allocator; + u8 *m_header; + int m_packet_size; public: - virtual ~Packet(); + BasePacket(mem::StandardAllocator *allocator, int packet_size) : m_allocator(allocator), m_header(nullptr), m_packet_size(packet_size) { + AMS_ASSERT(packet_size >= static_cast(sizeof(HeaderType))); + + m_header = static_cast(m_allocator->Allocate(m_packet_size)); + } + + virtual ~BasePacket() { + if (m_header != nullptr) { + m_allocator->Free(m_header); + } + } + + bool IsAllocationSucceeded() const { + return m_header != nullptr; + } + + HeaderType *GetHeader() { + return reinterpret_cast(m_header); + } + + const HeaderType *GetHeader() const { + return reinterpret_cast(m_header); + } + + int GetBodySize() const { + return m_packet_size - sizeof(HeaderType); + } + + u8 *GetBody() { + if (this->GetBodySize() > 0) { + return m_header + sizeof(HeaderType); + } else { + return nullptr; + } + } + + void CopyBody(const void *src, int src_size) { + AMS_ASSERT(this->GetBodySize() >= 0); + + std::memcpy(this->GetBody(), src, src_size); + } + }; + + class Packet : public BasePacket, public util::IntrusiveListBaseNode { + public: + using BasePacket::BasePacket; }; struct PacketDeleter { diff --git a/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp b/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp index 469ab3334..e21d3ab36 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet_factory.cpp @@ -22,4 +22,76 @@ namespace ams::htclow { PacketDeleter{m_allocator}(packet); } + std::unique_ptr PacketFactory::MakeSendPacketCommon(impl::ChannelInternalType channel, s16 version, int body_size) { + /* Allocate memory for the packet. */ + if (void *buffer = m_allocator->Allocate(sizeof(Packet), alignof(Packet)); buffer != nullptr) { + /* Convert the buffer to a packet. */ + Packet *packet = static_cast(buffer); + + /* Construct the packet. */ + std::construct_at(packet, m_allocator, body_size + sizeof(PacketHeader)); + + /* Create the unique pointer. */ + std::unique_ptr ptr(packet, PacketDeleter{m_allocator}); + + /* Set packet header fields. */ + if (ptr && ptr->IsAllocationSucceeded()) { + PacketHeader *header = ptr->GetHeader(); + + header->signature = HtcGen2Signature; + header->offset = 0; + header->reserved = 0; + header->body_size = body_size; + header->version = version; + header->channel = channel; + header->share = 0; + } + + return ptr; + } else { + return std::unique_ptr(nullptr, PacketDeleter{m_allocator}); + } + } + + std::unique_ptr PacketFactory::MakeDataPacket(impl::ChannelInternalType channel, s16 version, const void *body, int body_size, u64 share, u32 offset) { + auto packet = this->MakeSendPacketCommon(channel, version, body_size); + if (packet) { + PacketHeader *header = packet->GetHeader(); + + header->packet_type = PacketType_Data; + header->offset = offset; + header->share = share; + + packet->CopyBody(body, body_size); + + AMS_ASSERT(packet->GetBodySize() == body_size); + } + + return packet; + } + + + std::unique_ptr PacketFactory::MakeMaxDataPacket(impl::ChannelInternalType channel, s16 version, u64 share) { + auto packet = this->MakeSendPacketCommon(channel, version, 0); + if (packet) { + PacketHeader *header = packet->GetHeader(); + + header->packet_type = PacketType_MaxData; + header->share = share; + } + + return packet; + } + + std::unique_ptr PacketFactory::MakeErrorPacket(impl::ChannelInternalType channel) { + auto packet = this->MakeSendPacketCommon(channel, 0, 0); + if (packet) { + PacketHeader *header = packet->GetHeader(); + + header->packet_type = PacketType_Error; + } + + return packet; + } + } diff --git a/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp index 8def47b9f..b14945dd2 100644 --- a/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_packet_factory.hpp @@ -25,7 +25,13 @@ namespace ams::htclow { public: PacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) { /* ... */ } + std::unique_ptr MakeDataPacket(impl::ChannelInternalType channel, s16 version, const void *body, int body_size, u64 share, u32 offset); + std::unique_ptr MakeMaxDataPacket(impl::ChannelInternalType channel, s16 version, u64 share); + std::unique_ptr MakeErrorPacket(impl::ChannelInternalType channel); + void Delete(Packet *packet); + private: + std::unique_ptr MakeSendPacketCommon(impl::ChannelInternalType channel, s16 version, int body_size); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.cpp b/libraries/libstratosphere/source/htclow/htclow_worker.cpp index 714ebee64..d5bc43592 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.cpp @@ -82,8 +82,55 @@ namespace ams::htclow { } Result Worker::ProcessReceive() { - /* TODO */ - AMS_ABORT("Worker::ProcessReceive"); + /* Forever receive packets. */ + u8 packet_header_storage[sizeof(m_packet_header)]; + while (true) { + /* Receive the packet header. */ + R_TRY(m_driver->Receive(packet_header_storage, sizeof(packet_header_storage))); + + /* Check if the packet is a control packet. */ + if (ctrl::HtcctrlPacketHeader *ctrl_header = reinterpret_cast(packet_header_storage); ctrl_header->signature == ctrl::HtcctrlSignature) { + /* Process the packet. */ + R_TRY(this->ProcessReceive(*ctrl_header)); + } else { + /* Otherwise, we must have a normal packet. */ + PacketHeader *header = reinterpret_cast(packet_header_storage); + R_UNLESS(header->signature == HtcGen2Signature, htclow::ResultProtocolError()); + + /* Process the packet. */ + R_TRY(this->ProcessReceive(*header)); + } + } + } + + Result Worker::ProcessReceive(const ctrl::HtcctrlPacketHeader &header) { + /* Check the header. */ + R_TRY(m_service->CheckReceivedHeader(header)); + + /* Receive the body, if we have one. */ + if (header.body_size > 0) { + R_TRY(m_driver->Receive(m_receive_packet_body, header.body_size)); + } + + /* Process the received packet. */ + m_service->ProcessReceivePacket(header, m_receive_packet_body, header.body_size); + + return ResultSuccess(); + } + + Result Worker::ProcessReceive(const PacketHeader &header) { + /* Check the header. */ + R_TRY(m_mux->CheckReceivedHeader(header)); + + /* Receive the body, if we have one. */ + if (header.body_size > 0) { + R_TRY(m_driver->Receive(m_receive_packet_body, header.body_size)); + } + + /* Process the received packet. */ + m_mux->ProcessReceivePacket(header, m_receive_packet_body, header.body_size); + + return ResultSuccess(); } Result Worker::ProcessSend() { diff --git a/libraries/libstratosphere/source/htclow/htclow_worker.hpp b/libraries/libstratosphere/source/htclow/htclow_worker.hpp index a30214117..1e9529f1f 100644 --- a/libraries/libstratosphere/source/htclow/htclow_worker.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_worker.hpp @@ -22,9 +22,14 @@ namespace ams::htclow { class Worker { + private: + static_assert(sizeof(ctrl::HtcctrlPacketHeader) <= sizeof(PacketHeader)); + static_assert(sizeof(ctrl::HtcctrlPacketBody) <= sizeof(PacketBody)); private: u32 m_thread_stack_size; - u8 m_04[0x7C024]; /* TODO... not knowing what an almost 128 KB field is is embarassing. */ + u8 m_packet_header[sizeof(PacketHeader)]; + u8 m_send_packet_body[sizeof(PacketBody)]; + u8 m_receive_packet_body[sizeof(PacketBody)]; mem::StandardAllocator *m_allocator; mux::Mux *m_mux; ctrl::HtcctrlService *m_service; @@ -58,6 +63,9 @@ namespace ams::htclow { Result ProcessReceive(); Result ProcessSend(); + + Result ProcessReceive(const ctrl::HtcctrlPacketHeader &header); + Result ProcessReceive(const PacketHeader &header); }; } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 1b6d5a43d..9db51a24f 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -38,6 +38,46 @@ namespace ams::htclow::mux { } } + Result Mux::CheckReceivedHeader(const PacketHeader &header) const { + /* Check the packet signature. */ + AMS_ASSERT(header.signature == HtcGen2Signature); + + /* Switch on the packet type. */ + switch (header.packet_type) { + case PacketType_Data: + R_UNLESS(header.version == m_version, htclow::ResultProtocolError()); + R_UNLESS(header.body_size <= sizeof(PacketBody), htclow::ResultProtocolError()); + break; + case PacketType_MaxData: + R_UNLESS(header.version == m_version, htclow::ResultProtocolError()); + R_UNLESS(header.body_size == 0, htclow::ResultProtocolError()); + break; + case PacketType_Error: + R_UNLESS(header.body_size == 0, htclow::ResultProtocolError()); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + Result Mux::ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Process for the channel. */ + if (m_channel_impl_map.Exists(header.channel)) { + R_TRY(this->CheckChannelExist(header.channel)); + + return m_channel_impl_map[header.channel].ProcessReceivePacket(header, body, body_size); + } else { + if (header.packet_type == PacketType_Data || header.packet_type == PacketType_MaxData) { + this->SendErrorPacket(header.channel); + } + return htclow::ResultChannelNotExist(); + } + } + void Mux::UpdateChannelState() { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); @@ -62,4 +102,14 @@ namespace ams::htclow::mux { } } + Result Mux::CheckChannelExist(impl::ChannelInternalType channel) { + R_UNLESS(m_channel_impl_map.Exists(channel), htclow::ResultChannelNotExist()); + return ResultSuccess(); + } + + Result Mux::SendErrorPacket(impl::ChannelInternalType channel) { + /* TODO */ + AMS_ABORT("Mux::SendErrorPacket"); + } + } diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index f9e6180a9..8a41df89e 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -37,8 +37,15 @@ namespace ams::htclow::mux { void SetVersion(u16 version); + Result CheckReceivedHeader(const PacketHeader &header) const; + Result ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size); + void UpdateChannelState(); void UpdateMuxState(); + private: + Result CheckChannelExist(impl::ChannelInternalType channel); + + Result SendErrorPacket(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 fe54c8ada..9e523c1ac 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.cpp @@ -28,6 +28,110 @@ namespace ams::htclow::mux { m_send_buffer.SetVersion(version); } + Result ChannelImpl::CheckState(std::initializer_list states) const { + /* Determine if we have a matching state. */ + bool match = false; + for (const auto &state : states) { + match |= m_state == state; + } + + /* If we do, we're good. */ + R_SUCCEED_IF(match); + + /* Otherwise, return appropriate failure error. */ + if (m_state == ChannelState_Disconnected) { + return htclow::ResultInvalidChannelStateDisconnected(); + } else { + return htclow::ResultInvalidChannelState(); + } + } + + Result ChannelImpl::CheckPacketVersion(s16 version) const { + R_UNLESS(version == m_version, htclow::ResultChannelVersionNotMatched()); + return ResultSuccess(); + } + + + Result ChannelImpl::ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size) { + switch (header.packet_type) { + case PacketType_Data: + return this->ProcessReceiveDataPacket(header.version, header.share, header.offset, body, body_size); + case PacketType_MaxData: + return this->ProcessReceiveMaxDataPacket(header.version, header.share); + case PacketType_Error: + return this->ProcessReceiveErrorPacket(); + default: + return htclow::ResultProtocolError(); + } + } + + Result ChannelImpl::ProcessReceiveDataPacket(s16 version, u64 share, u32 offset, const void *body, size_t body_size) { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connectable, ChannelState_Connected})); + + /* Check the packet version. */ + R_TRY(this->CheckPacketVersion(version)); + + /* Check that offset matches. */ + R_UNLESS(offset == static_cast(m_offset), htclow::ResultProtocolError()); + + /* Check for flow control, if we should. */ + if (m_config.flow_control_enabled) { + /* Check that the share increases monotonically. */ + if (m_share.has_value()) { + R_UNLESS(m_share.value() <= share, htclow::ResultProtocolError()); + } + + /* Update our share. */ + m_share = share; + + /* Signal our event. */ + this->SignalSendPacketEvent(); + } + + /* Update our offset. */ + m_offset += body_size; + + /* Write the packet body. */ + R_ABORT_UNLESS(m_receive_buffer.Write(body, body_size)); + + /* Notify the data was received. */ + m_task_manager->NotifyReceiveData(m_channel, m_receive_buffer.GetDataSize()); + + return ResultSuccess(); + } + + Result ChannelImpl::ProcessReceiveMaxDataPacket(s16 version, u64 share) { + /* Check our state. */ + R_TRY(this->CheckState({ChannelState_Connectable, ChannelState_Connected})); + + /* Check the packet version. */ + R_TRY(this->CheckPacketVersion(version)); + + /* Check for flow control, if we should. */ + if (m_config.flow_control_enabled) { + /* Check that the share increases monotonically. */ + if (m_share.has_value()) { + R_UNLESS(m_share.value() <= share, htclow::ResultProtocolError()); + } + + /* Update our share. */ + m_share = share; + + /* Signal our event. */ + this->SignalSendPacketEvent(); + } + + return ResultSuccess(); + } + + Result ChannelImpl::ProcessReceiveErrorPacket() { + if (m_state == ChannelState_Connected || m_state == ChannelState_Disconnected) { + this->ShutdownForce(); + } + return ResultSuccess(); + } + void ChannelImpl::UpdateState() { /* Check if shutdown must be forced. */ if (m_state_machine->IsUnsupportedServiceChannelToShutdown(m_channel)) { @@ -83,4 +187,10 @@ namespace ams::htclow::mux { } } + void ChannelImpl::SignalSendPacketEvent() { + if (m_event != nullptr) { + m_event->Signal(); + } + } + } 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 b24a115a8..bc78d4a6f 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_channel_impl.hpp @@ -42,9 +42,10 @@ namespace ams::htclow::mux { SendBuffer m_send_buffer; RingBuffer m_receive_buffer; s16 m_version; - /* TODO: Channel config */ + ChannelConfig m_config; /* TODO: tracking variables. */ - std::optional m_108; + u64 m_offset; + std::optional m_share; os::Event m_state_change_event; ChannelState m_state; public: @@ -52,11 +53,22 @@ namespace ams::htclow::mux { void SetVersion(s16 version); + Result ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size); + void UpdateState(); private: void ShutdownForce(); void SetState(ChannelState state); void SetStateWithoutCheck(ChannelState state); + + void SignalSendPacketEvent(); + + Result CheckState(std::initializer_list states) const; + Result CheckPacketVersion(s16 version) const; + + Result ProcessReceiveDataPacket(s16 version, u64 share, u32 offset, const void *body, size_t body_size); + Result ProcessReceiveMaxDataPacket(s16 version, u64 share); + Result ProcessReceiveErrorPacket(); }; } 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 14886b152..3b193975e 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 @@ -41,6 +41,10 @@ namespace ams::htclow::mux { ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev); ChannelImpl &GetChannelImpl(impl::ChannelInternalType channel); + + bool Exists(impl::ChannelInternalType channel) const { + return m_map.find(channel) != m_map.end(); + } private: ChannelImpl &GetChannelImpl(int index); public: diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp new file mode 100644 index 000000000..bf76300cb --- /dev/null +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp @@ -0,0 +1,48 @@ +/* + * 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_ring_buffer.hpp" + +namespace ams::htclow::mux { + + Result RingBuffer::Write(const void *data, size_t size) { + /* Validate pre-conditions. */ + AMS_ASSERT(!m_is_read_only); + + /* Check that our buffer can hold the data. */ + R_UNLESS(m_buffer != nullptr, htclow::ResultChannelBufferOverflow()); + R_UNLESS(m_data_size + size <= m_buffer_size, htclow::ResultChannelBufferOverflow()); + + /* Determine position and copy sizes. */ + const size_t pos = (m_data_size + m_offset) % m_buffer_size; + const size_t left = m_buffer_size - pos; + const size_t over = size - left; + + /* Copy. */ + if (left != 0) { + std::memcpy(static_cast(m_buffer) + pos, data, left); + } + if (over != 0) { + std::memcpy(m_buffer, static_cast(data) + left, over); + } + + /* Update our data size. */ + m_data_size += size; + + return ResultSuccess(); + } + +} 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 02557ea68..7cd451f89 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.hpp @@ -29,6 +29,10 @@ namespace ams::htclow::mux { bool m_has_copied; public: RingBuffer() : m_buffer(), m_read_only_buffer(), m_is_read_only(true), m_buffer_size(), m_data_size(), m_offset(), m_has_copied(false) { /* ... */ } + + 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_task_manager.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.cpp index f396a9cd4..3e2046acf 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,14 @@ namespace ams::htclow::mux { } } + void TaskManager::NotifyReceiveData(impl::ChannelInternalType channel, size_t size) { + for (auto i = 0; i < MaxTaskCount; ++i) { + if (m_valid[i] && m_tasks[i].channel == channel && m_tasks[i].size <= size) { + this->CompleteTask(i, EventTrigger_ReceiveData); + } + } + } + void TaskManager::NotifyConnectReady() { for (auto i = 0; i < MaxTaskCount; ++i) { if (m_valid[i] && m_tasks[i].type == TaskType_Connect) { 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 d82a97dae..9c5c03423 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_task_manager.hpp @@ -22,6 +22,7 @@ namespace ams::htclow::mux { enum EventTrigger : u8 { EventTrigger_Disconnect = 1, + EventTrigger_ReceiveData = 2, EventTrigger_ConnectReady = 11, }; @@ -40,7 +41,7 @@ namespace ams::htclow::mux { bool has_event_trigger; EventTrigger event_trigger; TaskType type; - u64 _38; + size_t size; }; private: bool m_valid[MaxTaskCount]; @@ -49,6 +50,7 @@ namespace ams::htclow::mux { TaskManager() : m_valid() { /* ... */ } void NotifyDisconnect(impl::ChannelInternalType channel); + void NotifyReceiveData(impl::ChannelInternalType channel, size_t size); 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 4e47508c8..383d343c4 100644 --- a/libraries/libvapours/include/vapours/results/htclow_results.hpp +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -20,13 +20,26 @@ namespace ams::htclow { R_DEFINE_NAMESPACE_RESULT_MODULE(29); - R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); + R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); + R_DEFINE_ERROR_RESULT(ChannelNotExist, 10); + + R_DEFINE_ERROR_RESULT(InvalidChannelState, 200); + R_DEFINE_ERROR_RESULT(InvalidChannelStateDisconnected, 201); R_DEFINE_ERROR_RANGE(InternalError, 1000, 2999); R_DEFINE_ERROR_RESULT(OutOfMemory, 1002); R_DEFINE_ERROR_RESULT(InvalidArgument, 1003); + R_DEFINE_ERROR_RESULT(ProtocolError, 1004); R_DEFINE_ERROR_RESULT(Cancelled, 1005); + R_DEFINE_ERROR_RANGE(MuxError, 1100, 1199); + R_DEFINE_ERROR_RESULT(ChannelBufferOverflow, 1101); + R_DEFINE_ERROR_RESULT(ChannelBufferHasNotEnoughData, 1102); + R_DEFINE_ERROR_RESULT(ChannelVersionNotMatched, 1103); + R_DEFINE_ERROR_RESULT(ChannelStateTransitionError, 1104); + R_DEFINE_ERROR_RESULT(ChannelReceiveBufferEmpty, 1106); + R_DEFINE_ERROR_RESULT(ChannelSequenceIdNotMatched, 1107); + R_DEFINE_ERROR_RANGE(DriverError, 1200, 1999); R_DEFINE_ERROR_RESULT(DriverOpened, 1201);