htc: implement htcfs protocol bringup

This commit is contained in:
Michael Scire 2021-02-12 22:07:34 -08:00 committed by SciresM
parent 99a38dce32
commit 5c97469348
10 changed files with 316 additions and 7 deletions

View file

@ -15,6 +15,7 @@
*/ */
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include "htcfs_client_impl.hpp" #include "htcfs_client_impl.hpp"
#include "htcfs_result.hpp"
#include "../htclow/htclow_default_channel_config.hpp" #include "../htclow/htclow_default_channel_config.hpp"
namespace ams::htcfs { namespace ams::htcfs {
@ -153,8 +154,19 @@ namespace ams::htcfs {
} }
Result ClientImpl::SetUpProtocol() { Result ClientImpl::SetUpProtocol() {
/* TODO: Actual client <-> host RPC here. */ /* Get the maximum supported protocol on the host side. */
m_header_factory.SetVersion(1); 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(); return ResultSuccess();
} }
@ -163,4 +175,112 @@ namespace ams::htcfs {
m_header_factory.SetVersion(0); 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<const u8 *>(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<u8 *>(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();
}
} }

View file

@ -57,6 +57,17 @@ namespace ams::htcfs {
Result SetUpProtocol(); Result SetUpProtocol();
void TearDownProtocol(); 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);
}; };
} }

View file

@ -18,6 +18,63 @@
namespace ams::htcfs { 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<Header>::value);
static_assert(sizeof(Header) == 0x40);
class HeaderFactory { class HeaderFactory {
private: private:
s16 m_version; s16 m_version;
@ -26,6 +83,34 @@ namespace ams::htcfs {
public: public:
s16 GetVersion() const { return m_version; } s16 GetVersion() const { return m_version; }
void SetVersion(s16 version) { m_version = 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);
}
}; };
} }

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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<HtcfsResult>(param));
}
}

View file

@ -114,7 +114,7 @@ namespace ams::htclow {
return ResultSuccess(); 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. */ /* Check pre-conditions. */
AMS_ASSERT(util::IsIntValueRepresentable<size_t>(size)); AMS_ASSERT(util::IsIntValueRepresentable<size_t>(size));

View file

@ -40,7 +40,7 @@ namespace ams::htclow {
void Shutdown(); void Shutdown();
Result Receive(s64 *out, void *dst, s64 size, ReceiveOption option); 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 SetConfig(const ChannelConfig &config);
void SetReceiveBuffer(void *buf, size_t size); void SetReceiveBuffer(void *buf, size_t size);

View file

@ -67,7 +67,7 @@ namespace ams::htclow::mux {
/* Determine position and copy sizes. */ /* Determine position and copy sizes. */
const size_t pos = (m_data_size + m_offset) % m_buffer_size; 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; const size_t over = size - left;
/* Copy. */ /* Copy. */

View file

@ -82,7 +82,7 @@ namespace ams::htclow::mux {
/* Check that we have data. */ /* Check that we have data. */
const auto ring_buffer_data_size = m_ring_buffer.GetDataSize(); const auto ring_buffer_data_size = m_ring_buffer.GetDataSize();
if (ring_buffer_data_size > 0) { if (ring_buffer_data_size == 0) {
return false; return false;
} }
@ -102,7 +102,7 @@ namespace ams::htclow::mux {
const auto data_size = std::min(sendable_size, m_max_packet_size); const auto data_size = std::min(sendable_size, m_max_packet_size);
/* Make data packet header. */ /* 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. */ /* Copy the data. */
R_ABORT_UNLESS(m_ring_buffer.Copy(body, data_size)); R_ABORT_UNLESS(m_ring_buffer.Copy(body, data_size));

View file

@ -37,6 +37,7 @@
#include <vapours/results/gpio_results.hpp> #include <vapours/results/gpio_results.hpp>
#include <vapours/results/hipc_results.hpp> #include <vapours/results/hipc_results.hpp>
#include <vapours/results/htc_results.hpp> #include <vapours/results/htc_results.hpp>
#include <vapours/results/htcfs_results.hpp>
#include <vapours/results/htclow_results.hpp> #include <vapours/results/htclow_results.hpp>
#include <vapours/results/i2c_results.hpp> #include <vapours/results/i2c_results.hpp>
#include <vapours/results/kvdb_results.hpp> #include <vapours/results/kvdb_results.hpp>

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/results/results_common.hpp>
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);
}