Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,426 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_mux.hpp"
|
||||
#include "../htclow_packet_factory.hpp"
|
||||
#include "../ctrl/htclow_ctrl_state_machine.hpp"
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
Mux::Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm)
|
||||
: m_packet_factory(pf), m_state_machine(sm), m_task_manager(), m_event(os::EventClearMode_ManualClear),
|
||||
m_channel_impl_map(pf, sm, std::addressof(m_task_manager), std::addressof(m_event)), m_global_send_buffer(pf),
|
||||
m_mutex(), m_state(MuxState::Normal), m_version(ProtocolVersion)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void Mux::SetVersion(u16 version) {
|
||||
/* Set our version. */
|
||||
m_version = version;
|
||||
|
||||
/* Set all entries in our map. */
|
||||
/* NOTE: Nintendo does this highly inefficiently... */
|
||||
for (auto &pair : m_channel_impl_map.GetMap()) {
|
||||
m_channel_impl_map[pair.second].SetVersion(m_version);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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 (auto it = m_channel_impl_map.GetMap().find(header.channel); it != m_channel_impl_map.GetMap().end()) {
|
||||
R_RETURN(m_channel_impl_map[it->second].ProcessReceivePacket(header, body, body_size));
|
||||
} else {
|
||||
if (header.packet_type == PacketType_Data || header.packet_type == PacketType_MaxData) {
|
||||
this->SendErrorPacket(header.channel);
|
||||
}
|
||||
R_THROW(htclow::ResultChannelNotExist());
|
||||
}
|
||||
}
|
||||
|
||||
bool Mux::QuerySendPacket(PacketHeader *header, PacketBody *body, int *out_body_size) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Check for an error packet. */
|
||||
/* NOTE: Nintendo checks this once per iteration of the below loop. */
|
||||
/* The extra checks are unnecessary, because we hold our mutex. */
|
||||
if (auto *error_packet = m_global_send_buffer.GetNextPacket(); error_packet != nullptr) {
|
||||
std::memcpy(header, error_packet->GetHeader(), sizeof(*header));
|
||||
*out_body_size = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Iterate the map, checking each channel for a valid valid packet. */
|
||||
for (auto &pair : m_channel_impl_map.GetMap()) {
|
||||
/* Get the current channel impl. */
|
||||
/* See if the channel has something for us to send. */
|
||||
if (m_channel_impl_map[pair.second].QuerySendPacket(header, body, out_body_size)) {
|
||||
return this->IsSendable(header->packet_type);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Mux::RemovePacket(const PacketHeader &header) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Remove the packet from the appropriate source. */
|
||||
if (header.packet_type == PacketType_Error) {
|
||||
m_global_send_buffer.RemovePacket();
|
||||
} else if (auto it = m_channel_impl_map.GetMap().find(header.channel); it != m_channel_impl_map.GetMap().end()) {
|
||||
m_channel_impl_map[it->second].RemovePacket(header);
|
||||
}
|
||||
|
||||
/* Notify the task manager. */
|
||||
m_task_manager.NotifySendReady();
|
||||
}
|
||||
|
||||
void Mux::UpdateChannelState() {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Update the state of all channels in our map. */
|
||||
/* NOTE: Nintendo does this highly inefficiently... */
|
||||
for (auto pair : m_channel_impl_map.GetMap()) {
|
||||
m_channel_impl_map[pair.second].UpdateState();
|
||||
}
|
||||
}
|
||||
|
||||
void Mux::UpdateMuxState() {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Update whether we're sleeping. */
|
||||
if (m_state_machine->IsSleeping()) {
|
||||
m_state = MuxState::Sleep;
|
||||
} else {
|
||||
m_state = MuxState::Normal;
|
||||
m_event.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
Result Mux::CheckChannelExist(impl::ChannelInternalType channel) {
|
||||
R_UNLESS(m_channel_impl_map.Exists(channel), htclow::ResultChannelNotExist());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Mux::SendErrorPacket(impl::ChannelInternalType channel) {
|
||||
/* Create and send the packet. */
|
||||
R_TRY(m_global_send_buffer.AddPacket(m_packet_factory->MakeErrorPacket(channel)));
|
||||
|
||||
/* Signal our event. */
|
||||
m_event.Signal();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool Mux::IsSendable(PacketType packet_type) const {
|
||||
AMS_UNUSED(packet_type);
|
||||
|
||||
switch (m_state) {
|
||||
case MuxState::Normal:
|
||||
return true;
|
||||
case MuxState::Sleep:
|
||||
return false;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
Result Mux::Open(impl::ChannelInternalType channel) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Check that the channel doesn't already exist. */
|
||||
R_UNLESS(!m_channel_impl_map.Exists(channel), htclow::ResultChannelAlreadyExist());
|
||||
|
||||
/* Add the channel. */
|
||||
R_TRY(m_channel_impl_map.AddChannel(channel));
|
||||
|
||||
/* Set the channel version. */
|
||||
m_channel_impl_map.GetChannelImpl(channel).SetVersion(m_version);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Mux::Close(impl::ChannelInternalType channel) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* If we have the channel, close it. */
|
||||
if (auto it = m_channel_impl_map.GetMap().find(channel); it != m_channel_impl_map.GetMap().end()) {
|
||||
/* Shut down the channel. */
|
||||
m_channel_impl_map[it->second].ShutdownForce();
|
||||
|
||||
/* Remove the channel. */
|
||||
R_ABORT_UNLESS(m_channel_impl_map.RemoveChannel(channel));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Mux::ConnectBegin(u32 *out_task_id, impl::ChannelInternalType channel) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Find the channel. */
|
||||
auto it = m_channel_impl_map.GetMap().find(channel);
|
||||
R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist());
|
||||
|
||||
/* Perform the connection. */
|
||||
R_RETURN(m_channel_impl_map[it->second].DoConnectBegin(out_task_id));
|
||||
}
|
||||
|
||||
Result Mux::ConnectEnd(impl::ChannelInternalType channel, u32 task_id) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Get the trigger for the task. */
|
||||
const auto trigger = m_task_manager.GetTrigger(task_id);
|
||||
|
||||
/* Free the task. */
|
||||
m_task_manager.FreeTask(task_id);
|
||||
|
||||
/* Check that we didn't hit a disconnect. */
|
||||
R_UNLESS(trigger != EventTrigger_Disconnect, htclow::ResultInvalidChannelStateDisconnected());
|
||||
|
||||
/* Find the channel. */
|
||||
auto it = m_channel_impl_map.GetMap().find(channel);
|
||||
R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist());
|
||||
|
||||
/* Perform the disconnection. */
|
||||
R_RETURN(m_channel_impl_map[it->second].DoConnectEnd());
|
||||
}
|
||||
|
||||
ChannelState Mux::GetChannelState(impl::ChannelInternalType channel) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return m_channel_impl_map.GetChannelImpl(channel).GetChannelState();
|
||||
}
|
||||
|
||||
os::EventType *Mux::GetChannelStateEvent(impl::ChannelInternalType channel) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return m_channel_impl_map.GetChannelImpl(channel).GetChannelStateEvent();
|
||||
}
|
||||
|
||||
Result Mux::FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Find the channel. */
|
||||
auto it = m_channel_impl_map.GetMap().find(channel);
|
||||
R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist());
|
||||
|
||||
/* Perform the connection. */
|
||||
R_RETURN(m_channel_impl_map[it->second].DoFlush(out_task_id));
|
||||
}
|
||||
|
||||
Result Mux::FlushEnd(u32 task_id) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Get the trigger for the task. */
|
||||
const auto trigger = m_task_manager.GetTrigger(task_id);
|
||||
|
||||
/* Free the task. */
|
||||
m_task_manager.FreeTask(task_id);
|
||||
|
||||
/* Check that we didn't hit a disconnect. */
|
||||
R_UNLESS(trigger != EventTrigger_Disconnect, htclow::ResultInvalidChannelStateDisconnected());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
os::EventType *Mux::GetTaskEvent(u32 task_id) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return m_task_manager.GetTaskEvent(task_id);
|
||||
}
|
||||
|
||||
Result Mux::ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Find the channel. */
|
||||
auto it = m_channel_impl_map.GetMap().find(channel);
|
||||
R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist());
|
||||
|
||||
/* Perform the connection. */
|
||||
R_RETURN(m_channel_impl_map[it->second].DoReceiveBegin(out_task_id, size));
|
||||
}
|
||||
|
||||
Result Mux::ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Free the task. */
|
||||
m_task_manager.FreeTask(task_id);
|
||||
|
||||
/* If we have data, perform the receive. */
|
||||
if (dst_size > 0) {
|
||||
/* Find the channel. */
|
||||
auto it = m_channel_impl_map.GetMap().find(channel);
|
||||
R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist());
|
||||
|
||||
/* Perform the receive. */
|
||||
R_RETURN(m_channel_impl_map[it->second].DoReceiveEnd(out, dst, dst_size));
|
||||
} else {
|
||||
*out = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
|
||||
Result Mux::SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Find the channel. */
|
||||
auto it = m_channel_impl_map.GetMap().find(channel);
|
||||
R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist());
|
||||
|
||||
/* Perform the connection. */
|
||||
R_RETURN(m_channel_impl_map[it->second].DoSend(out_task_id, out, src, src_size));
|
||||
}
|
||||
|
||||
Result Mux::SendEnd(u32 task_id) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Get the trigger for the task. */
|
||||
const auto trigger = m_task_manager.GetTrigger(task_id);
|
||||
|
||||
/* Free the task. */
|
||||
m_task_manager.FreeTask(task_id);
|
||||
|
||||
/* Check that we didn't hit a disconnect. */
|
||||
R_UNLESS(trigger != EventTrigger_Disconnect, htclow::ResultInvalidChannelStateDisconnected());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Mux::WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size) {
|
||||
R_RETURN(this->ReceiveBegin(out_task_id, channel, size));
|
||||
}
|
||||
|
||||
Result Mux::WaitReceiveEnd(u32 task_id) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Get the trigger for the task. */
|
||||
const auto trigger = m_task_manager.GetTrigger(task_id);
|
||||
|
||||
/* Free the task. */
|
||||
m_task_manager.FreeTask(task_id);
|
||||
|
||||
/* Check that we didn't hit a disconnect. */
|
||||
R_UNLESS(trigger != EventTrigger_Disconnect, htclow::ResultInvalidChannelStateDisconnected());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Mux::SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Find the channel. */
|
||||
auto it = m_channel_impl_map.GetMap().find(channel);
|
||||
AMS_ABORT_UNLESS(it != m_channel_impl_map.GetMap().end());
|
||||
|
||||
/* Perform the connection. */
|
||||
return m_channel_impl_map[it->second].SetConfig(config);
|
||||
}
|
||||
|
||||
void Mux::SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size, size_t max_packet_size) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Find the channel. */
|
||||
auto it = m_channel_impl_map.GetMap().find(channel);
|
||||
AMS_ABORT_UNLESS(it != m_channel_impl_map.GetMap().end());
|
||||
|
||||
/* Set the send buffer. */
|
||||
m_channel_impl_map[it->second].SetSendBuffer(buf, buf_size, max_packet_size);
|
||||
}
|
||||
|
||||
void Mux::SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size, size_t max_packet_size) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Find the channel. */
|
||||
auto it = m_channel_impl_map.GetMap().find(channel);
|
||||
AMS_ABORT_UNLESS(it != m_channel_impl_map.GetMap().end());
|
||||
|
||||
/* Set the send buffer. */
|
||||
m_channel_impl_map[it->second].SetSendBufferWithData(buf, buf_size, max_packet_size);
|
||||
}
|
||||
|
||||
void Mux::SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Find the channel. */
|
||||
auto it = m_channel_impl_map.GetMap().find(channel);
|
||||
AMS_ABORT_UNLESS(it != m_channel_impl_map.GetMap().end());
|
||||
|
||||
/* Set the send buffer. */
|
||||
m_channel_impl_map[it->second].SetReceiveBuffer(buf, buf_size);
|
||||
}
|
||||
|
||||
Result Mux::Shutdown(impl::ChannelInternalType channel) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Find the channel. */
|
||||
auto it = m_channel_impl_map.GetMap().find(channel);
|
||||
R_UNLESS(it != m_channel_impl_map.GetMap().end(), htclow::ResultChannelNotExist());
|
||||
|
||||
/* Perform the shutdown. */
|
||||
R_RETURN(m_channel_impl_map[it->second].DoShutdown());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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 "htclow_mux_task_manager.hpp"
|
||||
#include "htclow_mux_channel_impl_map.hpp"
|
||||
#include "htclow_mux_global_send_buffer.hpp"
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
enum class MuxState {
|
||||
Normal,
|
||||
Sleep,
|
||||
};
|
||||
|
||||
class Mux {
|
||||
private:
|
||||
private:
|
||||
PacketFactory *m_packet_factory;
|
||||
ctrl::HtcctrlStateMachine *m_state_machine;
|
||||
TaskManager m_task_manager;
|
||||
os::Event m_event;
|
||||
ChannelImplMap m_channel_impl_map;
|
||||
GlobalSendBuffer m_global_send_buffer;
|
||||
os::SdkMutex m_mutex;
|
||||
MuxState m_state;
|
||||
s16 m_version;
|
||||
public:
|
||||
Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm);
|
||||
|
||||
void SetVersion(u16 version);
|
||||
|
||||
os::EventType *GetSendPacketEvent() { return m_event.GetBase(); }
|
||||
|
||||
Result CheckReceivedHeader(const PacketHeader &header) const;
|
||||
Result ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size);
|
||||
|
||||
bool QuerySendPacket(PacketHeader *header, PacketBody *body, int *out_body_size);
|
||||
void RemovePacket(const PacketHeader &header);
|
||||
|
||||
void UpdateChannelState();
|
||||
void UpdateMuxState();
|
||||
public:
|
||||
Result Open(impl::ChannelInternalType channel);
|
||||
Result Close(impl::ChannelInternalType channel);
|
||||
|
||||
Result ConnectBegin(u32 *out_task_id, impl::ChannelInternalType channel);
|
||||
Result ConnectEnd(impl::ChannelInternalType channel, u32 task_id);
|
||||
|
||||
ChannelState GetChannelState(impl::ChannelInternalType channel);
|
||||
os::EventType *GetChannelStateEvent(impl::ChannelInternalType channel);
|
||||
|
||||
Result FlushBegin(u32 *out_task_id, impl::ChannelInternalType channel);
|
||||
Result FlushEnd(u32 task_id);
|
||||
|
||||
os::EventType *GetTaskEvent(u32 task_id);
|
||||
|
||||
Result ReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size);
|
||||
Result ReceiveEnd(size_t *out, void *dst, size_t dst_size, impl::ChannelInternalType channel, u32 task_id);
|
||||
|
||||
Result SendBegin(u32 *out_task_id, size_t *out, const void *src, size_t src_size, impl::ChannelInternalType channel);
|
||||
Result SendEnd(u32 task_id);
|
||||
|
||||
Result WaitReceiveBegin(u32 *out_task_id, impl::ChannelInternalType channel, size_t size);
|
||||
Result WaitReceiveEnd(u32 task_id);
|
||||
|
||||
void SetConfig(impl::ChannelInternalType channel, const ChannelConfig &config);
|
||||
|
||||
void SetSendBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size, size_t max_packet_size);
|
||||
void SetReceiveBuffer(impl::ChannelInternalType channel, void *buf, size_t buf_size);
|
||||
void SetSendBufferWithData(impl::ChannelInternalType channel, const void *buf, size_t buf_size, size_t max_packet_size);
|
||||
|
||||
Result Shutdown(impl::ChannelInternalType channel);
|
||||
private:
|
||||
Result CheckChannelExist(impl::ChannelInternalType channel);
|
||||
|
||||
Result SendErrorPacket(impl::ChannelInternalType channel);
|
||||
|
||||
bool IsSendable(PacketType packet_type) const;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,473 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_mux_channel_impl.hpp"
|
||||
#include "../ctrl/htclow_ctrl_state_machine.hpp"
|
||||
#include "../htclow_default_channel_config.hpp"
|
||||
#include "../htclow_packet_factory.hpp"
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
ChannelImpl::ChannelImpl(impl::ChannelInternalType channel, PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev)
|
||||
: m_channel(channel), m_packet_factory(pf), m_state_machine(sm), m_task_manager(tm), m_event(ev),
|
||||
m_send_buffer(m_channel, pf), m_receive_buffer(), m_version(ProtocolVersion), m_config(DefaultChannelConfig),
|
||||
m_offset(0), m_total_send_size(0), m_cur_max_data(0), m_prev_max_data(0), m_share(),
|
||||
m_state_change_event(os::EventClearMode_ManualClear), m_state(ChannelState_Unconnectable)
|
||||
|
||||
{
|
||||
this->UpdateState();
|
||||
}
|
||||
|
||||
void ChannelImpl::SetVersion(s16 version) {
|
||||
/* Sanity check the version. */
|
||||
AMS_ASSERT(version <= ProtocolVersion);
|
||||
|
||||
/* Set version. */
|
||||
m_version = version;
|
||||
m_send_buffer.SetVersion(version);
|
||||
}
|
||||
|
||||
Result ChannelImpl::CheckState(std::initializer_list<ChannelState> 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) {
|
||||
R_THROW(htclow::ResultInvalidChannelStateDisconnected());
|
||||
} else {
|
||||
R_THROW(htclow::ResultInvalidChannelState());
|
||||
}
|
||||
}
|
||||
|
||||
Result ChannelImpl::CheckPacketVersion(s16 version) const {
|
||||
R_UNLESS(version == m_version, htclow::ResultChannelVersionNotMatched());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
Result ChannelImpl::ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size) {
|
||||
switch (header.packet_type) {
|
||||
case PacketType_Data:
|
||||
R_RETURN(this->ProcessReceiveDataPacket(header.version, header.share, header.offset, body, body_size));
|
||||
case PacketType_MaxData:
|
||||
R_RETURN(this->ProcessReceiveMaxDataPacket(header.version, header.share));
|
||||
case PacketType_Error:
|
||||
R_RETURN(this->ProcessReceiveErrorPacket());
|
||||
default:
|
||||
R_THROW(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<u32>(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());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ChannelImpl::ProcessReceiveErrorPacket() {
|
||||
if (m_state == ChannelState_Connected || m_state == ChannelState_Disconnected) {
|
||||
this->ShutdownForce();
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool ChannelImpl::QuerySendPacket(PacketHeader *header, PacketBody *body, int *out_body_size) {
|
||||
/* Check our send buffer. */
|
||||
if (m_send_buffer.QueryNextPacket(header, body, out_body_size, m_cur_max_data, m_total_send_size, m_share.has_value(), m_share.value_or(0))) {
|
||||
/* Update tracking variables. */
|
||||
if (header->packet_type == PacketType_Data) {
|
||||
m_prev_max_data = m_cur_max_data;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelImpl::RemovePacket(const PacketHeader &header) {
|
||||
/* Remove the packet. */
|
||||
m_send_buffer.RemovePacket(header);
|
||||
|
||||
/* Check if the send buffer is now empty. */
|
||||
if (m_send_buffer.Empty()) {
|
||||
m_task_manager->NotifySendBufferEmpty(m_channel);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelImpl::UpdateState() {
|
||||
/* Check if shutdown must be forced. */
|
||||
if (m_state_machine->IsUnsupportedServiceChannelToShutdown(m_channel)) {
|
||||
this->ShutdownForce();
|
||||
}
|
||||
|
||||
/* Check if we're readied. */
|
||||
if (m_state_machine->IsReadied()) {
|
||||
m_task_manager->NotifyConnectReady();
|
||||
}
|
||||
|
||||
/* Update our state transition. */
|
||||
if (m_state_machine->IsConnectable(m_channel)) {
|
||||
if (m_state == ChannelState_Unconnectable) {
|
||||
this->SetState(ChannelState_Connectable);
|
||||
}
|
||||
} else if (m_state_machine->IsUnconnectable()) {
|
||||
if (m_state == ChannelState_Connectable) {
|
||||
this->SetState(ChannelState_Unconnectable);
|
||||
m_state_machine->SetNotConnecting(m_channel);
|
||||
} else if (m_state == ChannelState_Connected) {
|
||||
this->ShutdownForce();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelImpl::ShutdownForce() {
|
||||
/* Clear our send buffer. */
|
||||
m_send_buffer.Clear();
|
||||
|
||||
/* Set our state to shutdown. */
|
||||
this->SetState(ChannelState_Disconnected);
|
||||
}
|
||||
|
||||
void ChannelImpl::SetState(ChannelState state) {
|
||||
/* Check that we can perform the transition. */
|
||||
AMS_ABORT_UNLESS(IsStateTransitionAllowed(m_state, state));
|
||||
|
||||
/* Perform the transition. */
|
||||
this->SetStateWithoutCheck(state);
|
||||
}
|
||||
|
||||
void ChannelImpl::SetStateWithoutCheck(ChannelState state) {
|
||||
/* Change our state. */
|
||||
if (m_state != state) {
|
||||
m_state = state;
|
||||
m_state_change_event.Signal();
|
||||
}
|
||||
|
||||
/* If relevant, notify disconnect. */
|
||||
if (m_state == ChannelState_Disconnected) {
|
||||
m_task_manager->NotifyDisconnect(m_channel);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelImpl::SignalSendPacketEvent() {
|
||||
if (m_event != nullptr) {
|
||||
m_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
Result ChannelImpl::DoConnectBegin(u32 *out_task_id) {
|
||||
/* Check our state. */
|
||||
R_TRY(this->CheckState({ChannelState_Connectable}));
|
||||
|
||||
/* Set ourselves as connecting. */
|
||||
m_state_machine->SetConnecting(m_channel);
|
||||
|
||||
/* Allocate a task. */
|
||||
u32 task_id{};
|
||||
R_TRY(m_task_manager->AllocateTask(std::addressof(task_id), m_channel));
|
||||
|
||||
/* Configure the task. */
|
||||
m_task_manager->ConfigureConnectTask(task_id);
|
||||
|
||||
/* If we're ready, complete the task immediately. */
|
||||
if (m_state_machine->IsReadied()) {
|
||||
m_task_manager->CompleteTask(task_id, EventTrigger_ConnectReady);
|
||||
}
|
||||
|
||||
/* Set the output task id. */
|
||||
*out_task_id = task_id;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ChannelImpl::DoConnectEnd() {
|
||||
/* Check our state. */
|
||||
R_TRY(this->CheckState({ChannelState_Connectable}));
|
||||
|
||||
/* Perform handshake, if we should. */
|
||||
if (m_config.handshake_enabled) {
|
||||
/* Set our current max data. */
|
||||
m_cur_max_data = m_receive_buffer.GetBufferSize();
|
||||
|
||||
/* Make a max data packet. */
|
||||
auto packet = m_packet_factory->MakeMaxDataPacket(m_channel, m_version, m_cur_max_data);
|
||||
R_UNLESS(packet, htclow::ResultOutOfMemory());
|
||||
|
||||
/* Send the packet. */
|
||||
m_send_buffer.AddPacket(std::move(packet));
|
||||
|
||||
/* Signal that we have an packet to send. */
|
||||
this->SignalSendPacketEvent();
|
||||
|
||||
/* Set our prev max data. */
|
||||
m_prev_max_data = m_cur_max_data;
|
||||
} else {
|
||||
/* Set our share. */
|
||||
m_share = m_config.initial_counter_max_data;
|
||||
|
||||
/* If we're not empty, signal. */
|
||||
if (!m_send_buffer.Empty()) {
|
||||
this->SignalSendPacketEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/* Set our state as connected. */
|
||||
this->SetState(ChannelState_Connected);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ChannelImpl::DoFlush(u32 *out_task_id) {
|
||||
/* Check our state. */
|
||||
R_TRY(this->CheckState({ChannelState_Connected}));
|
||||
|
||||
/* Allocate a task. */
|
||||
u32 task_id{};
|
||||
R_TRY(m_task_manager->AllocateTask(std::addressof(task_id), m_channel));
|
||||
|
||||
/* Configure the task. */
|
||||
m_task_manager->ConfigureFlushTask(task_id);
|
||||
|
||||
/* If we're already flushed, complete the task immediately. */
|
||||
if (m_send_buffer.Empty()) {
|
||||
m_task_manager->CompleteTask(task_id, EventTrigger_SendBufferEmpty);
|
||||
}
|
||||
|
||||
/* Set the output task id. */
|
||||
*out_task_id = task_id;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ChannelImpl::DoReceiveBegin(u32 *out_task_id, size_t size) {
|
||||
/* Check our state. */
|
||||
R_TRY(this->CheckState({ChannelState_Connected, ChannelState_Disconnected}));
|
||||
|
||||
/* Allocate a task. */
|
||||
u32 task_id{};
|
||||
R_TRY(m_task_manager->AllocateTask(std::addressof(task_id), m_channel));
|
||||
|
||||
/* Configure the task. */
|
||||
m_task_manager->ConfigureReceiveTask(task_id, size);
|
||||
|
||||
/* Check if the task is already complete. */
|
||||
if (m_receive_buffer.GetDataSize() >= size) {
|
||||
m_task_manager->CompleteTask(task_id, EventTrigger_ReceiveData);
|
||||
} else if (m_state == ChannelState_Disconnected) {
|
||||
m_task_manager->CompleteTask(task_id, EventTrigger_Disconnect);
|
||||
}
|
||||
|
||||
/* Set the output task id. */
|
||||
*out_task_id = task_id;
|
||||
R_SUCCEED();
|
||||
|
||||
}
|
||||
Result ChannelImpl::DoReceiveEnd(size_t *out, void *dst, size_t dst_size) {
|
||||
/* Check our state. */
|
||||
R_TRY(this->CheckState({ChannelState_Connected, ChannelState_Disconnected}));
|
||||
|
||||
/* If we have nowhere to receive, we're done. */
|
||||
if (dst_size == 0) {
|
||||
*out = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Get the amount of receivable data. */
|
||||
const size_t receivable = m_receive_buffer.GetDataSize();
|
||||
const size_t received = std::min(dst_size, receivable);
|
||||
|
||||
/* Read the data. */
|
||||
R_ABORT_UNLESS(m_receive_buffer.Read(dst, received));
|
||||
|
||||
/* Handle flow control, if we should. */
|
||||
if (m_config.flow_control_enabled) {
|
||||
/* Read our fields. */
|
||||
const auto prev_max_data = m_prev_max_data;
|
||||
const auto next_max_data = m_cur_max_data + received;
|
||||
const auto max_packet_size = m_config.max_packet_size;
|
||||
const auto offset = m_offset;
|
||||
|
||||
/* Update our current max data. */
|
||||
m_cur_max_data = next_max_data;
|
||||
|
||||
/* If we can, send a max data packet. */
|
||||
if (prev_max_data - offset < max_packet_size + sizeof(PacketHeader)) {
|
||||
/* Make a max data packet. */
|
||||
auto packet = m_packet_factory->MakeMaxDataPacket(m_channel, m_version, next_max_data);
|
||||
R_UNLESS(packet, htclow::ResultOutOfMemory());
|
||||
|
||||
/* Send the packet. */
|
||||
m_send_buffer.AddPacket(std::move(packet));
|
||||
|
||||
/* Signal that we have an packet to send. */
|
||||
this->SignalSendPacketEvent();
|
||||
|
||||
/* Set our prev max data. */
|
||||
m_prev_max_data = m_cur_max_data;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the output size. */
|
||||
*out = received;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ChannelImpl::DoSend(u32 *out_task_id, size_t *out, const void *src, size_t src_size) {
|
||||
/* Check our state. */
|
||||
R_TRY(this->CheckState({ChannelState_Connected}));
|
||||
|
||||
/* Allocate a task. */
|
||||
u32 task_id{};
|
||||
R_TRY(m_task_manager->AllocateTask(std::addressof(task_id), m_channel));
|
||||
|
||||
/* Send the data. */
|
||||
const size_t sent = m_send_buffer.AddData(src, src_size);
|
||||
|
||||
/* Add the size to our total. */
|
||||
m_total_send_size += sent;
|
||||
|
||||
/* Signal our event. */
|
||||
this->SignalSendPacketEvent();
|
||||
|
||||
/* Configure the task. */
|
||||
m_task_manager->ConfigureSendTask(task_id);
|
||||
|
||||
/* If we sent all the data, we're done. */
|
||||
if (sent == src_size) {
|
||||
m_task_manager->CompleteTask(task_id, EventTrigger_SendComplete);
|
||||
}
|
||||
|
||||
/* Set the output. */
|
||||
*out_task_id = task_id;
|
||||
*out = sent;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ChannelImpl::DoShutdown() {
|
||||
/* Check our state. */
|
||||
R_TRY(this->CheckState({ChannelState_Connected}));
|
||||
|
||||
/* Set our state. */
|
||||
this->SetState(ChannelState_Disconnected);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void ChannelImpl::SetConfig(const ChannelConfig &config) {
|
||||
/* Check our state. */
|
||||
R_ABORT_UNLESS(this->CheckState({ChannelState_Unconnectable, ChannelState_Connectable}));
|
||||
|
||||
/* Set our config. */
|
||||
m_config = config;
|
||||
|
||||
/* Set flow control for our send buffer. */
|
||||
m_send_buffer.SetFlowControlEnabled(m_config.flow_control_enabled);
|
||||
}
|
||||
|
||||
void ChannelImpl::SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size) {
|
||||
/* Set buffer. */
|
||||
m_send_buffer.SetBuffer(buf, buf_size);
|
||||
|
||||
/* Determine true max packet size. */
|
||||
if (m_config.flow_control_enabled) {
|
||||
max_packet_size = std::min(max_packet_size, m_config.max_packet_size);
|
||||
}
|
||||
|
||||
/* Set max packet size. */
|
||||
m_send_buffer.SetMaxPacketSize(max_packet_size);
|
||||
}
|
||||
|
||||
void ChannelImpl::SetReceiveBuffer(void *buf, size_t buf_size) {
|
||||
/* Set the buffer. */
|
||||
m_receive_buffer.Initialize(buf, buf_size);
|
||||
}
|
||||
|
||||
void ChannelImpl::SetSendBufferWithData(const void *buf, size_t buf_size, size_t max_packet_size) {
|
||||
/* Set buffer. */
|
||||
m_send_buffer.SetReadOnlyBuffer(buf, buf_size);
|
||||
|
||||
/* Determine true max packet size. */
|
||||
if (m_config.flow_control_enabled) {
|
||||
max_packet_size = std::min(max_packet_size, m_config.max_packet_size);
|
||||
}
|
||||
|
||||
/* Set max packet size. */
|
||||
m_send_buffer.SetMaxPacketSize(max_packet_size);
|
||||
|
||||
/* Set our total send size. */
|
||||
m_total_send_size = buf_size;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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 "htclow_mux_task_manager.hpp"
|
||||
#include "htclow_mux_send_buffer.hpp"
|
||||
|
||||
namespace ams::htclow {
|
||||
|
||||
class PacketFactory;
|
||||
|
||||
namespace ctrl {
|
||||
|
||||
class HtcctrlStateMachine;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
class ChannelImpl {
|
||||
private:
|
||||
impl::ChannelInternalType m_channel;
|
||||
PacketFactory *m_packet_factory;
|
||||
ctrl::HtcctrlStateMachine *m_state_machine;
|
||||
TaskManager *m_task_manager;
|
||||
os::Event *m_event;
|
||||
SendBuffer m_send_buffer;
|
||||
RingBuffer m_receive_buffer;
|
||||
s16 m_version;
|
||||
ChannelConfig m_config;
|
||||
u64 m_offset;
|
||||
u64 m_total_send_size;
|
||||
u64 m_cur_max_data;
|
||||
u64 m_prev_max_data;
|
||||
util::optional<u64> m_share;
|
||||
os::Event m_state_change_event;
|
||||
ChannelState m_state;
|
||||
public:
|
||||
ChannelImpl(impl::ChannelInternalType channel, PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev);
|
||||
|
||||
void SetVersion(s16 version);
|
||||
|
||||
ChannelState GetChannelState() { return m_state; }
|
||||
os::EventType *GetChannelStateEvent() { return m_state_change_event.GetBase(); }
|
||||
|
||||
Result ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size);
|
||||
|
||||
bool QuerySendPacket(PacketHeader *header, PacketBody *body, int *out_body_size);
|
||||
|
||||
void RemovePacket(const PacketHeader &header);
|
||||
|
||||
void ShutdownForce();
|
||||
|
||||
void UpdateState();
|
||||
public:
|
||||
Result DoConnectBegin(u32 *out_task_id);
|
||||
Result DoConnectEnd();
|
||||
|
||||
Result DoFlush(u32 *out_task_id);
|
||||
|
||||
Result DoReceiveBegin(u32 *out_task_id, size_t size);
|
||||
Result DoReceiveEnd(size_t *out, void *dst, size_t dst_size);
|
||||
|
||||
Result DoSend(u32 *out_task_id, size_t *out, const void *src, size_t src_size);
|
||||
|
||||
Result DoShutdown();
|
||||
|
||||
void SetConfig(const ChannelConfig &config);
|
||||
|
||||
void SetSendBuffer(void *buf, size_t buf_size, size_t max_packet_size);
|
||||
void SetReceiveBuffer(void *buf, size_t buf_size);
|
||||
void SetSendBufferWithData(const void *buf, size_t buf_size, size_t max_packet_size);
|
||||
private:
|
||||
void SetState(ChannelState state);
|
||||
void SetStateWithoutCheck(ChannelState state);
|
||||
|
||||
void SignalSendPacketEvent();
|
||||
|
||||
Result CheckState(std::initializer_list<ChannelState> 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();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_mux_channel_impl_map.hpp"
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
ChannelImplMap::ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev)
|
||||
: m_packet_factory(pf), m_state_machine(sm), m_task_manager(tm), m_event(ev), m_map()
|
||||
{
|
||||
/* Initialize the map. */
|
||||
m_map.Initialize(MaxChannelCount, m_map_buffer, sizeof(m_map_buffer));
|
||||
|
||||
/* Set all storages as invalid. */
|
||||
for (auto i = 0; i < MaxChannelCount; ++i) {
|
||||
m_storage_valid[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
ChannelImpl &ChannelImplMap::GetChannelImpl(int index) {
|
||||
return GetReference(m_channel_storage[index]);
|
||||
}
|
||||
|
||||
ChannelImpl &ChannelImplMap::GetChannelImpl(impl::ChannelInternalType channel) {
|
||||
/* Find the channel. */
|
||||
auto it = m_map.find(channel);
|
||||
AMS_ASSERT(it != m_map.end());
|
||||
|
||||
/* Return the implementation object. */
|
||||
return this->GetChannelImpl(it->second);
|
||||
}
|
||||
|
||||
Result ChannelImplMap::AddChannel(impl::ChannelInternalType channel) {
|
||||
/* Find a free storage. */
|
||||
int idx;
|
||||
for (idx = 0; idx < MaxChannelCount; ++idx) {
|
||||
if (!m_storage_valid[idx]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate that the storage is free. */
|
||||
R_UNLESS(idx < MaxChannelCount, htclow::ResultOutOfChannel());
|
||||
|
||||
/* Create the channel impl. */
|
||||
util::ConstructAt(m_channel_storage[idx], channel, m_packet_factory, m_state_machine, m_task_manager, m_event);
|
||||
|
||||
/* Mark the storage valid. */
|
||||
m_storage_valid[idx] = true;
|
||||
|
||||
/* Insert into our map. */
|
||||
m_map.insert(std::pair<const impl::ChannelInternalType, int>{channel, idx});
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ChannelImplMap::RemoveChannel(impl::ChannelInternalType channel) {
|
||||
/* Find the storage. */
|
||||
auto it = m_map.find(channel);
|
||||
AMS_ASSERT(it != m_map.end());
|
||||
|
||||
/* Get the channel index. */
|
||||
const auto index = it->second;
|
||||
AMS_ASSERT(0 <= index && index < MaxChannelCount);
|
||||
|
||||
/* Get the channel impl. */
|
||||
auto *channel_impl = GetPointer(m_channel_storage[index]);
|
||||
|
||||
/* Mark the storage as invalid. */
|
||||
m_storage_valid[index] = false;
|
||||
|
||||
/* Erase the channel from the map. */
|
||||
m_map.erase(channel);
|
||||
|
||||
/* Destroy the channel. */
|
||||
std::destroy_at(channel_impl);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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 "htclow_mux_channel_impl.hpp"
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
class ChannelImplMap {
|
||||
NON_COPYABLE(ChannelImplMap);
|
||||
NON_MOVEABLE(ChannelImplMap);
|
||||
public:
|
||||
static constexpr int MaxChannelCount = 64;
|
||||
|
||||
using MapType = util::FixedMap<impl::ChannelInternalType, int>;
|
||||
|
||||
static constexpr size_t MapRequiredMemorySize = MapType::GetRequiredMemorySize(MaxChannelCount);
|
||||
private:
|
||||
PacketFactory *m_packet_factory;
|
||||
ctrl::HtcctrlStateMachine *m_state_machine;
|
||||
TaskManager *m_task_manager;
|
||||
os::Event *m_event;
|
||||
u8 m_map_buffer[MapRequiredMemorySize];
|
||||
MapType m_map;
|
||||
util::TypedStorage<ChannelImpl> m_channel_storage[MaxChannelCount];
|
||||
bool m_storage_valid[MaxChannelCount];
|
||||
public:
|
||||
ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev);
|
||||
|
||||
ChannelImpl &GetChannelImpl(int index);
|
||||
ChannelImpl &GetChannelImpl(impl::ChannelInternalType channel);
|
||||
|
||||
bool Exists(impl::ChannelInternalType channel) const {
|
||||
return m_map.find(channel) != m_map.end();
|
||||
}
|
||||
|
||||
Result AddChannel(impl::ChannelInternalType channel);
|
||||
Result RemoveChannel(impl::ChannelInternalType channel);
|
||||
private:
|
||||
public:
|
||||
MapType &GetMap() {
|
||||
return m_map;
|
||||
}
|
||||
|
||||
ChannelImpl &operator[](int index) {
|
||||
return this->GetChannelImpl(index);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_mux_global_send_buffer.hpp"
|
||||
#include "../htclow_packet_factory.hpp"
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
Packet *GlobalSendBuffer::GetNextPacket() {
|
||||
if (!m_packet_list.empty()) {
|
||||
return std::addressof(m_packet_list.front());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Result GlobalSendBuffer::AddPacket(std::unique_ptr<Packet, PacketDeleter> ptr) {
|
||||
/* Global send buffer only supports adding error packets. */
|
||||
R_UNLESS(ptr->GetHeader()->packet_type == PacketType_Error, htclow::ResultInvalidArgument());
|
||||
|
||||
/* Check if we already have an error packet for the channel. */
|
||||
for (const auto &packet : m_packet_list) {
|
||||
R_SUCCEED_IF(packet.GetHeader()->channel == ptr->GetHeader()->channel);
|
||||
}
|
||||
|
||||
/* We don't, so push back a new one. */
|
||||
m_packet_list.push_back(*(ptr.release()));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void GlobalSendBuffer::RemovePacket() {
|
||||
auto *packet = std::addressof(m_packet_list.front());
|
||||
m_packet_list.pop_front();
|
||||
|
||||
m_packet_factory->Delete(packet);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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 "../htclow_packet.hpp"
|
||||
|
||||
namespace ams::htclow {
|
||||
|
||||
class PacketFactory;
|
||||
|
||||
}
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
class GlobalSendBuffer {
|
||||
private:
|
||||
using PacketList = util::IntrusiveListBaseTraits<Packet>::ListType;
|
||||
private:
|
||||
PacketFactory *m_packet_factory;
|
||||
PacketList m_packet_list;
|
||||
public:
|
||||
GlobalSendBuffer(PacketFactory *pf) : m_packet_factory(pf), m_packet_list() { /* ... */ }
|
||||
|
||||
Packet *GetNextPacket();
|
||||
|
||||
Result AddPacket(std::unique_ptr<Packet, PacketDeleter> ptr);
|
||||
void RemovePacket();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_mux_ring_buffer.hpp"
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
void RingBuffer::Initialize(void *buffer, size_t buffer_size) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_buffer == nullptr);
|
||||
AMS_ASSERT(m_read_only_buffer == nullptr);
|
||||
|
||||
/* Set our fields. */
|
||||
m_buffer = buffer;
|
||||
m_buffer_size = buffer_size;
|
||||
m_is_read_only = false;
|
||||
}
|
||||
|
||||
void RingBuffer::InitializeForReadOnly(const void *buffer, size_t buffer_size) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_buffer == nullptr);
|
||||
AMS_ASSERT(m_read_only_buffer == nullptr);
|
||||
|
||||
/* Set our fields. */
|
||||
m_read_only_buffer = const_cast<void *>(buffer);
|
||||
m_buffer_size = buffer_size;
|
||||
m_data_size = buffer_size;
|
||||
m_is_read_only = true;
|
||||
}
|
||||
|
||||
void RingBuffer::Clear() {
|
||||
m_data_size = 0;
|
||||
m_offset = 0;
|
||||
m_can_discard = false;
|
||||
}
|
||||
|
||||
Result RingBuffer::Read(void *dst, size_t size) {
|
||||
/* Copy the data. */
|
||||
R_TRY(this->Copy(dst, size));
|
||||
|
||||
/* Discard. */
|
||||
R_TRY(this->Discard(size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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 = std::min(m_buffer_size - pos, size);
|
||||
const size_t over = size - left;
|
||||
|
||||
/* Copy. */
|
||||
if (left != 0) {
|
||||
std::memcpy(static_cast<u8 *>(m_buffer) + pos, data, left);
|
||||
}
|
||||
if (over != 0) {
|
||||
std::memcpy(m_buffer, static_cast<const u8 *>(data) + left, over);
|
||||
}
|
||||
|
||||
/* Update our data size. */
|
||||
m_data_size += size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result RingBuffer::Copy(void *dst, size_t size) {
|
||||
/* Select buffer to discard from. */
|
||||
void *buffer = m_is_read_only ? m_read_only_buffer : m_buffer;
|
||||
R_UNLESS(buffer != nullptr, htclow::ResultChannelBufferHasNotEnoughData());
|
||||
|
||||
/* Verify that we have enough data. */
|
||||
R_UNLESS(m_data_size >= size, htclow::ResultChannelBufferHasNotEnoughData());
|
||||
|
||||
/* Determine position and copy sizes. */
|
||||
const size_t pos = m_offset;
|
||||
const size_t left = std::min(m_buffer_size - pos, size);
|
||||
const size_t over = size - left;
|
||||
|
||||
/* Copy. */
|
||||
if (left != 0) {
|
||||
std::memcpy(dst, static_cast<const u8 *>(buffer) + pos, left);
|
||||
}
|
||||
if (over != 0) {
|
||||
std::memcpy(static_cast<u8 *>(dst) + left, buffer, over);
|
||||
}
|
||||
|
||||
/* Mark that we can discard. */
|
||||
m_can_discard = true;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result RingBuffer::Discard(size_t size) {
|
||||
/* Select buffer to discard from. */
|
||||
void *buffer = m_is_read_only ? m_read_only_buffer : m_buffer;
|
||||
R_UNLESS(buffer != nullptr, htclow::ResultChannelBufferHasNotEnoughData());
|
||||
|
||||
/* Verify that the data we're discarding has been read. */
|
||||
R_UNLESS(m_can_discard, htclow::ResultChannelCannotDiscard());
|
||||
|
||||
/* Verify that we have enough data. */
|
||||
R_UNLESS(m_data_size >= size, htclow::ResultChannelBufferHasNotEnoughData());
|
||||
|
||||
/* Discard. */
|
||||
m_offset = (m_offset + size) % m_buffer_size;
|
||||
m_data_size -= size;
|
||||
m_can_discard = false;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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>
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
class RingBuffer {
|
||||
private:
|
||||
void *m_buffer;
|
||||
void *m_read_only_buffer;
|
||||
bool m_is_read_only;
|
||||
size_t m_buffer_size;
|
||||
size_t m_data_size;
|
||||
size_t m_offset;
|
||||
bool m_can_discard;
|
||||
public:
|
||||
RingBuffer() : m_buffer(), m_read_only_buffer(), m_is_read_only(true), m_buffer_size(), m_data_size(), m_offset(), m_can_discard(false) { /* ... */ }
|
||||
|
||||
void Initialize(void *buffer, size_t buffer_size);
|
||||
void InitializeForReadOnly(const void *buffer, size_t buffer_size);
|
||||
|
||||
void Clear();
|
||||
|
||||
size_t GetBufferSize() { return m_buffer_size; }
|
||||
size_t GetDataSize() { return m_data_size; }
|
||||
|
||||
Result Read(void *dst, size_t size);
|
||||
Result Write(const void *data, size_t size);
|
||||
|
||||
Result Copy(void *dst, size_t size);
|
||||
|
||||
Result Discard(size_t size);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_mux_channel_impl.hpp"
|
||||
#include "../htclow_packet_factory.hpp"
|
||||
#include "../htclow_default_channel_config.hpp"
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
SendBuffer::SendBuffer(impl::ChannelInternalType channel, PacketFactory *pf)
|
||||
: m_channel(channel), m_packet_factory(pf), m_ring_buffer(), m_packet_list(),
|
||||
m_version(ProtocolVersion), m_flow_control_enabled(true), m_max_packet_size(DefaultChannelConfig.max_packet_size)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
SendBuffer::~SendBuffer() {
|
||||
m_ring_buffer.Clear();
|
||||
this->Clear();
|
||||
}
|
||||
|
||||
bool SendBuffer::IsPriorPacket(PacketType packet_type) const {
|
||||
return packet_type == PacketType_MaxData;
|
||||
}
|
||||
|
||||
void SendBuffer::SetVersion(s16 version) {
|
||||
/* Set version. */
|
||||
m_version = version;
|
||||
}
|
||||
|
||||
void SendBuffer::SetFlowControlEnabled(bool en) {
|
||||
/* Set flow control enabled. */
|
||||
m_flow_control_enabled = en;
|
||||
}
|
||||
|
||||
void SendBuffer::MakeDataPacketHeader(PacketHeader *header, int body_size, s16 version, u64 share, u32 offset) const {
|
||||
/* Set all packet fields. */
|
||||
header->signature = HtcGen2Signature;
|
||||
header->offset = offset;
|
||||
header->reserved = 0;
|
||||
header->version = version;
|
||||
header->body_size = body_size;
|
||||
header->channel = m_channel;
|
||||
header->packet_type = PacketType_Data;
|
||||
header->share = share;
|
||||
}
|
||||
|
||||
void SendBuffer::CopyPacket(PacketHeader *header, PacketBody *body, int *out_body_size, const Packet &packet) {
|
||||
/* Get the body size. */
|
||||
const int body_size = packet.GetBodySize();
|
||||
AMS_ASSERT(0 <= body_size && body_size <= static_cast<int>(sizeof(*body)));
|
||||
|
||||
/* Copy the header. */
|
||||
std::memcpy(header, packet.GetHeader(), sizeof(*header));
|
||||
|
||||
/* Copy the body. */
|
||||
std::memcpy(body, packet.GetBody(), body_size);
|
||||
|
||||
/* Set the output body size. */
|
||||
*out_body_size = body_size;
|
||||
}
|
||||
|
||||
bool SendBuffer::QueryNextPacket(PacketHeader *header, PacketBody *body, int *out_body_size, u64 max_data, u64 total_send_size, bool has_share, u64 share) {
|
||||
/* Check for a max data packet. */
|
||||
if (!m_packet_list.empty()) {
|
||||
this->CopyPacket(header, body, out_body_size, m_packet_list.front());
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check that we have data. */
|
||||
const auto ring_buffer_data_size = m_ring_buffer.GetDataSize();
|
||||
if (ring_buffer_data_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that we're valid for flow control. */
|
||||
if (m_flow_control_enabled && !has_share) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Determine the sendable size. */
|
||||
const auto offset = total_send_size - ring_buffer_data_size;
|
||||
const auto sendable_size = m_flow_control_enabled ? std::min<size_t>(share - offset, ring_buffer_data_size) : ring_buffer_data_size;
|
||||
if (sendable_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We're additionally bound by the actual packet size. */
|
||||
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, offset);
|
||||
|
||||
/* Copy the data. */
|
||||
R_ABORT_UNLESS(m_ring_buffer.Copy(body, data_size));
|
||||
|
||||
/* Set output body size. */
|
||||
*out_body_size = data_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SendBuffer::AddPacket(std::unique_ptr<Packet, PacketDeleter> ptr) {
|
||||
/* Get the packet. */
|
||||
auto *packet = ptr.release();
|
||||
|
||||
/* Check the packet type. */
|
||||
AMS_ABORT_UNLESS(this->IsPriorPacket(packet->GetHeader()->packet_type));
|
||||
|
||||
/* Add the packet. */
|
||||
m_packet_list.push_back(*packet);
|
||||
}
|
||||
|
||||
void SendBuffer::RemovePacket(const PacketHeader &header) {
|
||||
/* Get the packet type. */
|
||||
const auto packet_type = header.packet_type;
|
||||
|
||||
if (this->IsPriorPacket(packet_type)) {
|
||||
/* Packet will be using our list. */
|
||||
auto *packet = std::addressof(m_packet_list.front());
|
||||
m_packet_list.pop_front();
|
||||
m_packet_factory->Delete(packet);
|
||||
} else {
|
||||
/* Packet managed by ring buffer. */
|
||||
AMS_ABORT_UNLESS(packet_type == PacketType_Data);
|
||||
|
||||
/* Discard the packet's data. */
|
||||
const Result result = m_ring_buffer.Discard(header.body_size);
|
||||
if (!htclow::ResultChannelCannotDiscard::Includes(result)) {
|
||||
R_ABORT_UNLESS(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t SendBuffer::AddData(const void *data, size_t size) {
|
||||
/* Determine how much to actually add. */
|
||||
size = std::min(size, m_ring_buffer.GetBufferSize() - m_ring_buffer.GetDataSize());
|
||||
|
||||
/* Write the data. */
|
||||
R_ABORT_UNLESS(m_ring_buffer.Write(data, size));
|
||||
|
||||
/* Return the size we wrote. */
|
||||
return size;
|
||||
}
|
||||
|
||||
void SendBuffer::SetBuffer(void *buffer, size_t buffer_size) {
|
||||
m_ring_buffer.Initialize(buffer, buffer_size);
|
||||
}
|
||||
|
||||
void SendBuffer::SetReadOnlyBuffer(const void *buffer, size_t buffer_size) {
|
||||
m_ring_buffer.InitializeForReadOnly(buffer, buffer_size);
|
||||
}
|
||||
|
||||
void SendBuffer::SetMaxPacketSize(size_t max_packet_size) {
|
||||
m_max_packet_size = max_packet_size;
|
||||
}
|
||||
|
||||
bool SendBuffer::Empty() {
|
||||
return m_packet_list.empty() && m_ring_buffer.GetDataSize() == 0;
|
||||
}
|
||||
|
||||
void SendBuffer::Clear() {
|
||||
while (!m_packet_list.empty()) {
|
||||
auto *packet = std::addressof(m_packet_list.front());
|
||||
m_packet_list.pop_front();
|
||||
m_packet_factory->Delete(packet);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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 "../htclow_packet.hpp"
|
||||
#include "htclow_mux_ring_buffer.hpp"
|
||||
|
||||
namespace ams::htclow {
|
||||
|
||||
class PacketFactory;
|
||||
|
||||
}
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
class SendBuffer {
|
||||
private:
|
||||
using PacketList = util::IntrusiveListBaseTraits<Packet>::ListType;
|
||||
private:
|
||||
impl::ChannelInternalType m_channel;
|
||||
PacketFactory *m_packet_factory;
|
||||
RingBuffer m_ring_buffer;
|
||||
PacketList m_packet_list;
|
||||
s16 m_version;
|
||||
bool m_flow_control_enabled;
|
||||
size_t m_max_packet_size;
|
||||
private:
|
||||
bool IsPriorPacket(PacketType packet_type) const;
|
||||
|
||||
void MakeDataPacketHeader(PacketHeader *header, int body_size, s16 version, u64 share, u32 offset) const;
|
||||
|
||||
void CopyPacket(PacketHeader *header, PacketBody *body, int *out_body_size, const Packet &packet);
|
||||
public:
|
||||
SendBuffer(impl::ChannelInternalType channel, PacketFactory *pf);
|
||||
~SendBuffer();
|
||||
|
||||
void SetVersion(s16 version);
|
||||
void SetFlowControlEnabled(bool en);
|
||||
|
||||
bool QueryNextPacket(PacketHeader *header, PacketBody *body, int *out_body_size, u64 max_data, u64 total_send_size, bool has_share, u64 share);
|
||||
|
||||
void AddPacket(std::unique_ptr<Packet, PacketDeleter> ptr);
|
||||
void RemovePacket(const PacketHeader &header);
|
||||
|
||||
size_t AddData(const void *data, size_t size);
|
||||
|
||||
void SetBuffer(void *buffer, size_t buffer_size);
|
||||
void SetReadOnlyBuffer(const void *buffer, size_t buffer_size);
|
||||
void SetMaxPacketSize(size_t max_packet_size);
|
||||
|
||||
bool Empty();
|
||||
|
||||
void Clear();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_mux_task_manager.hpp"
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
os::EventType *TaskManager::GetTaskEvent(u32 task_id) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(task_id < MaxTaskCount);
|
||||
AMS_ASSERT(m_valid[task_id]);
|
||||
|
||||
return std::addressof(m_tasks[task_id].event);
|
||||
}
|
||||
|
||||
EventTrigger TaskManager::GetTrigger(u32 task_id) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(task_id < MaxTaskCount);
|
||||
AMS_ASSERT(m_valid[task_id]);
|
||||
|
||||
return m_tasks[task_id].event_trigger;
|
||||
}
|
||||
|
||||
Result TaskManager::AllocateTask(u32 *out_task_id, impl::ChannelInternalType channel) {
|
||||
/* Find a free task. */
|
||||
u32 task_id = 0;
|
||||
for (task_id = 0; task_id < util::size(m_tasks); ++task_id) {
|
||||
if (!m_valid[task_id]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify the task is free. */
|
||||
R_UNLESS(task_id < util::size(m_tasks), htclow::ResultOutOfTask());
|
||||
|
||||
/* Mark the task as allocated. */
|
||||
m_valid[task_id] = true;
|
||||
|
||||
/* Setup the task. */
|
||||
m_tasks[task_id].channel = channel;
|
||||
m_tasks[task_id].has_event_trigger = false;
|
||||
os::InitializeEvent(std::addressof(m_tasks[task_id].event), false, os::EventClearMode_ManualClear);
|
||||
|
||||
/* Return the task id. */
|
||||
*out_task_id = task_id;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void TaskManager::FreeTask(u32 task_id) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(task_id < MaxTaskCount);
|
||||
|
||||
/* Invalidate the task. */
|
||||
if (m_valid[task_id]) {
|
||||
os::FinalizeEvent(std::addressof(m_tasks[task_id].event));
|
||||
m_valid[task_id] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::ConfigureConnectTask(u32 task_id) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(task_id < MaxTaskCount);
|
||||
AMS_ASSERT(m_valid[task_id]);
|
||||
|
||||
/* Set the task type. */
|
||||
m_tasks[task_id].type = TaskType_Connect;
|
||||
}
|
||||
|
||||
void TaskManager::ConfigureFlushTask(u32 task_id) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(task_id < MaxTaskCount);
|
||||
AMS_ASSERT(m_valid[task_id]);
|
||||
|
||||
/* Set the task type. */
|
||||
m_tasks[task_id].type = TaskType_Flush;
|
||||
}
|
||||
|
||||
void TaskManager::ConfigureReceiveTask(u32 task_id, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(task_id < MaxTaskCount);
|
||||
AMS_ASSERT(m_valid[task_id]);
|
||||
|
||||
/* Set the task type. */
|
||||
m_tasks[task_id].type = TaskType_Receive;
|
||||
|
||||
/* Set the task size. */
|
||||
m_tasks[task_id].size = size;
|
||||
}
|
||||
|
||||
void TaskManager::ConfigureSendTask(u32 task_id) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(task_id < MaxTaskCount);
|
||||
AMS_ASSERT(m_valid[task_id]);
|
||||
|
||||
/* Set the task type. */
|
||||
m_tasks[task_id].type = TaskType_Send;
|
||||
}
|
||||
|
||||
void TaskManager::NotifyDisconnect(impl::ChannelInternalType channel) {
|
||||
for (auto i = 0; i < MaxTaskCount; ++i) {
|
||||
if (m_valid[i] && m_tasks[i].channel == channel) {
|
||||
this->CompleteTask(i, EventTrigger_Disconnect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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].type == TaskType_Receive && m_tasks[i].size <= size) {
|
||||
this->CompleteTask(i, EventTrigger_ReceiveData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::NotifySendReady() {
|
||||
for (auto i = 0; i < MaxTaskCount; ++i) {
|
||||
if (m_valid[i] && m_tasks[i].type == TaskType_Send) {
|
||||
this->CompleteTask(i, EventTrigger_SendReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::NotifySendBufferEmpty(impl::ChannelInternalType channel) {
|
||||
for (auto i = 0; i < MaxTaskCount; ++i) {
|
||||
if (m_valid[i] && m_tasks[i].channel == channel && m_tasks[i].type == TaskType_Flush) {
|
||||
this->CompleteTask(i, EventTrigger_SendBufferEmpty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::NotifyConnectReady() {
|
||||
for (auto i = 0; i < MaxTaskCount; ++i) {
|
||||
if (m_valid[i] && m_tasks[i].type == TaskType_Connect) {
|
||||
this->CompleteTask(i, EventTrigger_ConnectReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::CompleteTask(int index, EventTrigger trigger) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(0 <= index && index < MaxTaskCount);
|
||||
AMS_ASSERT(m_valid[index]);
|
||||
|
||||
/* Complete the task. */
|
||||
if (!m_tasks[index].has_event_trigger) {
|
||||
m_tasks[index].has_event_trigger = true;
|
||||
m_tasks[index].event_trigger = trigger;
|
||||
os::SignalEvent(std::addressof(m_tasks[index].event));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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>
|
||||
|
||||
namespace ams::htclow::mux {
|
||||
|
||||
constexpr inline int MaxTaskCount = 0x80;
|
||||
|
||||
enum EventTrigger : u8 {
|
||||
EventTrigger_Disconnect = 1,
|
||||
EventTrigger_ReceiveData = 2,
|
||||
EventTrigger_SendComplete = 4,
|
||||
EventTrigger_SendReady = 5,
|
||||
EventTrigger_SendBufferEmpty = 10,
|
||||
EventTrigger_ConnectReady = 11,
|
||||
};
|
||||
|
||||
class TaskManager {
|
||||
private:
|
||||
enum TaskType : u8 {
|
||||
TaskType_Receive = 0,
|
||||
TaskType_Send = 1,
|
||||
TaskType_Flush = 6,
|
||||
TaskType_Connect = 7,
|
||||
};
|
||||
|
||||
struct Task {
|
||||
impl::ChannelInternalType channel;
|
||||
os::EventType event;
|
||||
bool has_event_trigger;
|
||||
EventTrigger event_trigger;
|
||||
TaskType type;
|
||||
size_t size;
|
||||
};
|
||||
private:
|
||||
bool m_valid[MaxTaskCount];
|
||||
Task m_tasks[MaxTaskCount];
|
||||
public:
|
||||
TaskManager() : m_valid() { /* ... */ }
|
||||
|
||||
Result AllocateTask(u32 *out_task_id, impl::ChannelInternalType channel);
|
||||
void FreeTask(u32 task_id);
|
||||
|
||||
os::EventType *GetTaskEvent(u32 task_id);
|
||||
EventTrigger GetTrigger(u32 task_id);
|
||||
|
||||
void ConfigureConnectTask(u32 task_id);
|
||||
void ConfigureFlushTask(u32 task_id);
|
||||
void ConfigureReceiveTask(u32 task_id, size_t size);
|
||||
void ConfigureSendTask(u32 task_id);
|
||||
|
||||
void NotifyDisconnect(impl::ChannelInternalType channel);
|
||||
void NotifyReceiveData(impl::ChannelInternalType channel, size_t size);
|
||||
void NotifySendReady();
|
||||
void NotifySendBufferEmpty(impl::ChannelInternalType channel);
|
||||
void NotifyConnectReady();
|
||||
|
||||
void CompleteTask(int index, EventTrigger trigger);
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user