Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"

This reverts commit 15b7df8ef1.
This commit is contained in:
souldbminersmwc
2025-11-09 16:14:52 -05:00
parent 22ec140738
commit 21a3f953d7
3804 changed files with 435 additions and 570162 deletions

View File

@@ -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());
}
}

View File

@@ -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;
};
}

View File

@@ -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;
}
}

View File

@@ -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();
};
}

View File

@@ -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();
}
}

View File

@@ -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);
}
};
}

View File

@@ -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);
}
}

View File

@@ -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();
};
}

View File

@@ -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();
}
}

View File

@@ -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);
};
}

View File

@@ -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);
}
}
}

View File

@@ -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();
};
}

View File

@@ -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));
}
}
}

View File

@@ -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);
};
}