From 5c97469348a7437919982c6af815f12b7a7313d4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 12 Feb 2021 22:07:34 -0800 Subject: [PATCH] htc: implement htcfs protocol bringup --- .../source/htcfs/htcfs_client_impl.cpp | 124 +++++++++++++++++- .../source/htcfs/htcfs_client_impl.hpp | 11 ++ .../source/htcfs/htcfs_header_factory.hpp | 85 ++++++++++++ .../source/htcfs/htcfs_result.hpp | 54 ++++++++ .../source/htclow/htclow_channel.cpp | 2 +- .../source/htclow/htclow_channel.hpp | 2 +- .../htclow/mux/htclow_mux_ring_buffer.cpp | 2 +- .../htclow/mux/htclow_mux_send_buffer.cpp | 4 +- .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/htcfs_results.hpp | 38 ++++++ 10 files changed, 316 insertions(+), 7 deletions(-) create mode 100644 libraries/libstratosphere/source/htcfs/htcfs_result.hpp create mode 100644 libraries/libvapours/include/vapours/results/htcfs_results.hpp diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index 7d75464fb..ca218350e 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -15,6 +15,7 @@ */ #include #include "htcfs_client_impl.hpp" +#include "htcfs_result.hpp" #include "../htclow/htclow_default_channel_config.hpp" namespace ams::htcfs { @@ -153,8 +154,19 @@ namespace ams::htcfs { } Result ClientImpl::SetUpProtocol() { - /* TODO: Actual client <-> host RPC here. */ - m_header_factory.SetVersion(1); + /* Get the maximum supported protocol on the host side. */ + s16 max_host_protocol; + R_TRY(this->GetMaxProtocolVersion(std::addressof(max_host_protocol))); + + /* Verify that the host protocol is >= 0. */ + R_UNLESS(max_host_protocol >= 0, htcfs::ResultUnsupportedProtocolVersion()); + + /* Inform the host what protocol we're using. */ + const auto use_version = std::min(MaxProtocolVersion, max_host_protocol); + R_TRY(this->SetProtocolVersion(use_version)); + + /* Set the version in our header factory. */ + m_header_factory.SetVersion(use_version); return ResultSuccess(); } @@ -163,4 +175,112 @@ namespace ams::htcfs { m_header_factory.SetVersion(0); } + Result ClientImpl::CheckResponseHeaderWithoutVersion(const Header &response, PacketType packet_type) { + /* Check the protocol. */ + R_UNLESS(response.protocol == HtcfsProtocol, htcfs::ResultUnexpectedResponseProtocolId()); + + /* Check the packet category. */ + R_UNLESS(response.packet_category == PacketCategory::Response, htcfs::ResultUnexpectedResponsePacketCategory()); + + /* Check the type. */ + R_UNLESS(response.packet_type == packet_type, htcfs::ResultUnexpectedResponsePacketType()); + + return ResultSuccess(); + } + + Result ClientImpl::GetMaxProtocolVersion(s16 *out) { + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeGetMaxProtocolVersionHeader(std::addressof(request)); + + /* Send the request to the host. */ + R_TRY(this->SendToRpcChannel(std::addressof(request), sizeof(request))); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeaderWithoutVersion(response, request.packet_type)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Set the maximum protocol version. */ + *out = response.params[1]; + + return ResultSuccess(); + } + + Result ClientImpl::SetProtocolVersion(s16 version) { + /* Create space for request and response. */ + Header request, response; + + /* Create header for the request. */ + m_header_factory.MakeSetProtocolVersionHeader(std::addressof(request), version); + + /* Send the request to the host. */ + R_TRY(this->SendToRpcChannel(std::addressof(request), sizeof(request))); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeaderWithoutVersion(response, request.packet_type)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + return ResultSuccess(); + } + + Result ClientImpl::SendToRpcChannel(const void *src, s64 size) { + return this->SendToHtclow(src, size, std::addressof(m_rpc_channel)); + } + + Result ClientImpl::ReceiveFromRpcChannel(void *dst, s64 size) { + return this->ReceiveFromHtclow(dst, size, std::addressof(m_rpc_channel)); + } + + Result ClientImpl::SendToHtclow(const void *src, s64 size, htclow::Channel *channel) { + /* Check size. */ + R_UNLESS(size >= 0, htcfs::ResultInvalidArgument()); + + /* Iteratively send. */ + s64 sent; + for (s64 total = 0; total < size; total += sent) { + /* Send the current batch of data. */ + R_TRY(channel->Send(std::addressof(sent), static_cast(src) + total, size - total)); + + /* Check that we sent the right amount. */ + R_UNLESS(sent == size - total, htcfs::ResultHtclowChannelClosed()); + } + + /* Flush. */ + R_TRY(channel->Flush()); + + return ResultSuccess(); + } + + Result ClientImpl::ReceiveFromHtclow(void *dst, s64 size, htclow::Channel *channel) { + /* Check size. */ + R_UNLESS(size >= 0, htcfs::ResultInvalidArgument()); + + /* Iteratively receive. */ + s64 received; + for (s64 total = 0; total < size; total += received) { + /* Receive the current batch of data. */ + R_TRY(channel->Receive(std::addressof(received), static_cast(dst) + total, size - total, htclow::ReceiveOption_ReceiveAllData)); + + /* Check that we received the right amount. */ + R_UNLESS(received == size - total, htcfs::ResultHtclowChannelClosed()); + } + + /* Flush. */ + R_TRY(channel->Flush()); + + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index 8403186d1..4e84b404e 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -57,6 +57,17 @@ namespace ams::htcfs { Result SetUpProtocol(); void TearDownProtocol(); + + Result CheckResponseHeaderWithoutVersion(const Header &response, PacketType packet_type); + + Result GetMaxProtocolVersion(s16 *out); + Result SetProtocolVersion(s16 version); + + Result SendToRpcChannel(const void *src, s64 size); + Result ReceiveFromRpcChannel(void *dst, s64 size); + + Result SendToHtclow(const void *src, s64 size, htclow::Channel *channel); + Result ReceiveFromHtclow(void *dst, s64 size, htclow::Channel *channel); }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index 8fe691f5b..2556abdfb 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -18,6 +18,63 @@ namespace ams::htcfs { + constexpr inline s16 HtcfsProtocol = 1; + constexpr inline s16 MaxProtocolVersion = 1; + + enum class PacketCategory : u16 { + Request = 0, + Response = 1, + }; + + enum class PacketType : u16 { + GetMaxProtocolVersion = 0, + SetProtocolVersion = 1, + GetEntryType = 16, + OpenFile = 32, + CloseFile = 33, + GetPriorityForFile = 34, + SetPriorityForFile = 35, + CreateFile = 36, + DeleteFile = 37, + RenameFile = 38, + FileExists = 39, + ReadFile = 40, + WriteFile = 41, + FlushFile = 42, + GetFileTimeStamp = 43, + GetFileSize = 44, + SetFileSize = 45, + ReadFileLarge = 46, + WriteFileLarge = 47, + OpenDirectory = 48, + CloseDirectory = 49, + GetPriorityForDirectory = 50, + SetPriorityForDirectory = 51, + CreateDirectory = 52, + DeleteDirectory = 53, + RenameDirectory = 54, + DirectoryExists = 55, + ReadDirectory = 56, + GetEntryCount = 57, + GetWorkingDirectory = 58, + GetWorkingDirectorySize = 59, + GetCaseSensitivePath = 60, + GetDiskFreeSpace = 61, + ReadDirectoryLarge = 62, + }; + + struct Header { + s16 protocol; + s16 version; + PacketCategory packet_category; + PacketType packet_type; + s64 body_size; + s64 params[5]; + s64 reserved; + }; + static_assert(util::is_pod
::value); + static_assert(sizeof(Header) == 0x40); + class HeaderFactory { private: s16 m_version; @@ -26,6 +83,34 @@ namespace ams::htcfs { public: s16 GetVersion() const { return m_version; } void SetVersion(s16 version) { m_version = version; } + public: + ALWAYS_INLINE void MakeRequestHeader(Header *out, PacketType packet_type, s64 body_size = 0, s64 param0 = 0, s64 param1 = 0, s64 param2 = 0, s64 param3 = 0, s64 param4 = 0) { + /* Set protocol and version. */ + out->protocol = HtcfsProtocol; + out->version = m_version; + + /* Set type and category. */ + out->packet_category = PacketCategory::Request; + out->packet_type = packet_type; + + /* Set body size. */ + out->body_size = body_size; + + /* Set params. */ + out->params[0] = param0; + out->params[1] = param1; + out->params[2] = param2; + out->params[3] = param3; + out->params[4] = param4; + } + + void MakeGetMaxProtocolVersionHeader(Header *out) { + return this->MakeRequestHeader(out, PacketType::GetMaxProtocolVersion, 0); + } + + void MakeSetProtocolVersionHeader(Header *out, s16 version) { + return this->MakeRequestHeader(out, PacketType::SetProtocolVersion, 0, version); + } }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_result.hpp b/libraries/libstratosphere/source/htcfs/htcfs_result.hpp new file mode 100644 index 000000000..8346e66f6 --- /dev/null +++ b/libraries/libstratosphere/source/htcfs/htcfs_result.hpp @@ -0,0 +1,54 @@ +/* + * 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 "htcfs_client_impl.hpp" + +namespace ams::htcfs { + + enum class HtcfsResult { + Success = 0, + UnknownError = 1, + UnsupportedProtocolVersion = 2, + InvalidRequest = 3, + InvalidHandle = 4, + OutOfHandle = 5, + }; + + inline Result ConvertHtcfsResult(HtcfsResult result) { + switch (result) { + case HtcfsResult::Success: + return ResultSuccess(); + case HtcfsResult::UnknownError: + return htcfs::ResultUnknownError(); + case HtcfsResult::UnsupportedProtocolVersion: + return htcfs::ResultUnsupportedProtocolVersion(); + case HtcfsResult::InvalidRequest: + return htcfs::ResultInvalidRequest(); + case HtcfsResult::InvalidHandle: + return htcfs::ResultInvalidHandle(); + case HtcfsResult::OutOfHandle: + return htcfs::ResultOutOfHandle(); + default: + return htcfs::ResultUnknownError(); + } + } + + inline Result ConvertHtcfsResult(s64 param) { + return ConvertHtcfsResult(static_cast(param)); + } + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_channel.cpp b/libraries/libstratosphere/source/htclow/htclow_channel.cpp index 830369e25..3c9046694 100644 --- a/libraries/libstratosphere/source/htclow/htclow_channel.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_channel.cpp @@ -114,7 +114,7 @@ namespace ams::htclow { return ResultSuccess(); } - Result Channel::Send(s64 *out, const void *src, s64 size, ReceiveOption option) { + Result Channel::Send(s64 *out, const void *src, s64 size) { /* Check pre-conditions. */ AMS_ASSERT(util::IsIntValueRepresentable(size)); diff --git a/libraries/libstratosphere/source/htclow/htclow_channel.hpp b/libraries/libstratosphere/source/htclow/htclow_channel.hpp index 2d99cc7c4..908384809 100644 --- a/libraries/libstratosphere/source/htclow/htclow_channel.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_channel.hpp @@ -40,7 +40,7 @@ namespace ams::htclow { void Shutdown(); Result Receive(s64 *out, void *dst, s64 size, ReceiveOption option); - Result Send(s64 *out, const void *src, s64 size, ReceiveOption option); + Result Send(s64 *out, const void *src, s64 size); void SetConfig(const ChannelConfig &config); void SetReceiveBuffer(void *buf, size_t size); 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 a772ce93c..40b780de8 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_ring_buffer.cpp @@ -67,7 +67,7 @@ namespace ams::htclow::mux { /* 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 left = std::min(m_buffer_size - pos, size); const size_t over = size - left; /* Copy. */ 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 214bc3032..a7add6b21 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux_send_buffer.cpp @@ -82,7 +82,7 @@ namespace ams::htclow::mux { /* Check that we have data. */ const auto ring_buffer_data_size = m_ring_buffer.GetDataSize(); - if (ring_buffer_data_size > 0) { + if (ring_buffer_data_size == 0) { return false; } @@ -102,7 +102,7 @@ namespace ams::htclow::mux { const auto data_size = std::min(sendable_size, m_max_packet_size); /* Make data packet header. */ - this->MakeDataPacketHeader(header, data_size, m_version, max_data, share); + this->MakeDataPacketHeader(header, data_size, m_version, max_data, offset); /* Copy the data. */ R_ABORT_UNLESS(m_ring_buffer.Copy(body, data_size)); diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 23b7e2e9a..003bacf25 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/htcfs_results.hpp b/libraries/libvapours/include/vapours/results/htcfs_results.hpp new file mode 100644 index 000000000..cfad9663b --- /dev/null +++ b/libraries/libvapours/include/vapours/results/htcfs_results.hpp @@ -0,0 +1,38 @@ +/* + * 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 + +namespace ams::htcfs { + + R_DEFINE_NAMESPACE_RESULT_MODULE(31); + + R_DEFINE_ERROR_RESULT(InvalidArgument, 3); + + R_DEFINE_ERROR_RESULT(HtclowChannelClosed, 101); + + R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolId, 111); + R_DEFINE_ERROR_RESULT(UnexpectedResponseProtocolVersion, 112); + R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketCategory, 113); + R_DEFINE_ERROR_RESULT(UnexpectedResponsePacketType, 114); + + R_DEFINE_ERROR_RESULT(UnknownError, 211); + R_DEFINE_ERROR_RESULT(UnsupportedProtocolVersion, 212); + R_DEFINE_ERROR_RESULT(InvalidRequest, 213); + R_DEFINE_ERROR_RESULT(InvalidHandle, 214); + R_DEFINE_ERROR_RESULT(OutOfHandle, 215); + +}