mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
htc: implement htcfs protocol bringup
This commit is contained in:
parent
99a38dce32
commit
5c97469348
10 changed files with 316 additions and 7 deletions
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
54
libraries/libstratosphere/source/htcfs/htcfs_result.hpp
Normal file
54
libraries/libstratosphere/source/htcfs/htcfs_result.hpp
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue