diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp index 320ac254b..ad5a73420 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.cpp @@ -23,6 +23,7 @@ namespace ams::htcfs { /* TODO: Move to a header? */ constexpr u16 RpcChannelId = 0; + constexpr u16 DataChannelId = 1; alignas(os::ThreadStackAlignment) constinit u8 g_monitor_thread_stack[os::MemoryPageSize]; @@ -266,6 +267,14 @@ namespace ams::htcfs { return this->ReceiveFromHtclow(dst, size, std::addressof(m_rpc_channel)); } + Result ClientImpl::ReceiveFromDataChannel(s64 size) { + return m_data_channel.WaitReceive(size); + } + + Result ClientImpl::SendToDataChannel() { + return m_data_channel.Flush(); + } + Result ClientImpl::SendToHtclow(const void *src, s64 size, htclow::Channel *channel) { /* Check size. */ R_UNLESS(size >= 0, htcfs::ResultInvalidArgument()); @@ -316,6 +325,52 @@ namespace ams::htcfs { return ResultSuccess(); } + void ClientImpl::InitializeDataChannelForReceive(void *dst, size_t size) { + /* Open the data channel. */ + R_ABORT_UNLESS(m_data_channel.Open(std::addressof(m_module), DataChannelId)); + + /* Set our config. */ + constexpr htclow::ChannelConfig BulkReceiveConfig = { + .flow_control_enabled = false, + .handshake_enabled = false, + .max_packet_size = 0x3E000, + }; + m_data_channel.SetConfig(BulkReceiveConfig); + + /* Set receive buffer. */ + m_data_channel.SetReceiveBuffer(dst, size); + + /* Connect. */ + R_ABORT_UNLESS(m_data_channel.Connect()); + } + + void ClientImpl::InitializeDataChannelForSend(const void *src, size_t size) { + /* Open the data channel. */ + R_ABORT_UNLESS(m_data_channel.Open(std::addressof(m_module), DataChannelId)); + + /* Check that the size is valid. */ + AMS_ASSERT(util::IsIntValueRepresentable(size)); + + /* Set our config. */ + constexpr htclow::ChannelConfig BulkSendConfig = { + .flow_control_enabled = false, + .handshake_enabled = false, + .max_packet_size = 0xE020, + }; + m_data_channel.SetConfig(BulkSendConfig); + + /* Set our send buffer. */ + m_data_channel.SetSendBufferWithData(src, size); + + /* Connect. */ + R_ABORT_UNLESS(m_data_channel.Connect()); + } + + void ClientImpl::FinalizeDataChannel() { + /* Close our data channel. */ + m_data_channel.Close(); + } + Result ClientImpl::SendRequest(const Header &request, const void *arg1, size_t arg1_size, const void *arg2, size_t arg2_size) { /* Try to perform an optimized send. */ if (sizeof(request) + arg1_size + arg2_size < sizeof(m_packet_buffer)) { @@ -492,13 +547,46 @@ namespace ams::htcfs { /* Initialize our rpc channel. */ R_TRY(this->InitializeRpcChannel()); + /* Setup data channel. */ + const bool use_data_channel = max_out_entries > 0; + if (use_data_channel) { + this->InitializeDataChannelForReceive(out_entries, max_out_entries * sizeof(*out_entries)); + } + ON_SCOPE_EXIT { if (use_data_channel) { this->FinalizeDataChannel(); } }; + /* Create space for request and response. */ Header request, response; /* Create header for the request. */ - m_header_factory.MakeReadDirectoryLargeHeader(std::addressof(request), handle, max_out_entries); + m_header_factory.MakeReadDirectoryLargeHeader(std::addressof(request), handle, max_out_entries, DataChannelId); - AMS_ABORT("TODO: Data channel setup/teardown"); + /* Send the request to the host. */ + R_TRY(this->SendRequest(request)); + + /* Receive response from the host. */ + R_TRY(this->ReceiveFromRpcChannel(std::addressof(response), sizeof(response))); + + /* Check the response header. */ + R_TRY(this->CheckResponseHeader(response, request.packet_type, 0)); + + /* Check that we succeeded. */ + R_TRY(ConvertHtcfsResult(response.params[0])); + + /* Check our operation's result. */ + R_TRY(ConvertNativeResult(response.params[1])); + + /* Check that the number of entries read is allowable. */ + R_UNLESS(static_cast(response.params[2]) <= max_out_entries, htcfs::ResultUnexpectedResponseBody()); + + /* Read the entries, if there are any. */ + if (response.params[2] > 0) { + R_TRY(this->ReceiveFromDataChannel(response.params[2] * sizeof(*out_entries))); + } + + /* Set the number of output entries. */ + *out = response.params[2]; + + return ResultSuccess(); } Result ClientImpl::GetPriorityForDirectory(s32 *out, s32 handle) { diff --git a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp index b241a461d..e66fc7642 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_client_impl.hpp @@ -82,12 +82,19 @@ namespace ams::htcfs { Result SendToRpcChannel(const void *src, s64 size); Result ReceiveFromRpcChannel(void *dst, s64 size); + Result ReceiveFromDataChannel(s64 size); + Result SendToDataChannel(); + Result SendToHtclow(const void *src, s64 size, htclow::Channel *channel); Result ReceiveFromHtclow(void *dst, s64 size, htclow::Channel *channel); Result SendRequest(const Header &request) { return this->SendRequest(request, nullptr, 0, nullptr, 0); } Result SendRequest(const Header &request, const void *arg1, size_t arg1_size) { return this->SendRequest(request, arg1, arg1_size, nullptr, 0); } Result SendRequest(const Header &request, const void *arg1, size_t arg1_size, const void *arg2, size_t arg2_size); + + void InitializeDataChannelForReceive(void *dst, size_t size); + void InitializeDataChannelForSend(const void *src, size_t size); + void FinalizeDataChannel(); }; } diff --git a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp index d7511e81f..8417d34a9 100644 --- a/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp +++ b/libraries/libstratosphere/source/htcfs/htcfs_header_factory.hpp @@ -131,8 +131,8 @@ namespace ams::htcfs { return this->MakeRequestHeader(out, PacketType::ReadDirectory, 0, handle, max_out_entries); } - void MakeReadDirectoryLargeHeader(Header *out, s32 handle, size_t max_out_entries) { - return this->MakeRequestHeader(out, PacketType::ReadDirectoryLarge, 0, handle, max_out_entries); + void MakeReadDirectoryLargeHeader(Header *out, s32 handle, size_t max_out_entries, u16 data_channel_id) { + return this->MakeRequestHeader(out, PacketType::ReadDirectoryLarge, 0, handle, max_out_entries, data_channel_id); } void MakeGetPriorityForDirectoryHeader(Header *out, s32 handle) { diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp index 220d28c62..416f8ec3b 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.cpp @@ -305,7 +305,7 @@ namespace ams::htclow::mux { auto it = m_channel_impl_map.GetMap().find(channel); R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist()); - /* Perform the RECEIVE. */ + /* Perform the receive. */ return m_channel_impl_map[it->second].DoReceiveEnd(out, dst, dst_size); } else { *out = 0;