diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp
index bb12eb3dd..0689920bf 100644
--- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp
+++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet.hpp
@@ -39,7 +39,7 @@ namespace ams::htclow::ctrl {
struct HtcctrlPacketHeader {
u32 signature;
- u32 offset;
+ u32 sequence_id;
u32 reserved;
u32 body_size;
s16 version;
diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.cpp
new file mode 100644
index 000000000..7c18ed574
--- /dev/null
+++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "htclow_ctrl_packet_factory.hpp"
+
+namespace ams::htclow::ctrl {
+
+ std::unique_ptr HtcctrlPacketFactory::MakeSendPacketCommon(int body_size) {
+ /* Allocate memory for the packet. */
+ if (void *buffer = m_allocator->Allocate(sizeof(HtcctrlPacket), alignof(HtcctrlPacket)); buffer != nullptr) {
+ /* Convert the buffer to a packet. */
+ HtcctrlPacket *packet = static_cast(buffer);
+
+ /* Construct the packet. */
+ std::construct_at(packet, m_allocator, body_size + sizeof(HtcctrlPacketHeader));
+
+ /* Create the unique pointer. */
+ std::unique_ptr ptr(packet, HtcctrlPacketDeleter{m_allocator});
+
+ /* Set packet header fields. */
+ if (ptr && ptr->IsAllocationSucceeded()) {
+ HtcctrlPacketHeader *header = ptr->GetHeader();
+
+ header->signature = HtcctrlSignature;
+ header->sequence_id = m_sequence_id++;
+ header->reserved = 0;
+ header->body_size = body_size;
+ header->version = 1;
+ header->channel = {};
+ header->share = 0;
+ }
+
+ return ptr;
+ } else {
+ return std::unique_ptr(nullptr, HtcctrlPacketDeleter{m_allocator});
+ }
+ }
+
+ std::unique_ptr HtcctrlPacketFactory::MakeSuspendPacket() {
+ auto packet = this->MakeSendPacketCommon(0);
+ if (packet && packet->IsAllocationSucceeded()) {
+ packet->GetHeader()->packet_type = HtcctrlPacketType_SuspendFromTarget;
+ }
+
+ return packet;
+ }
+
+ std::unique_ptr HtcctrlPacketFactory::MakeResumePacket() {
+ auto packet = this->MakeSendPacketCommon(0);
+ if (packet && packet->IsAllocationSucceeded()) {
+ packet->GetHeader()->packet_type = HtcctrlPacketType_SuspendFromTarget;
+ }
+
+ return packet;
+ }
+
+ std::unique_ptr HtcctrlPacketFactory::MakeReadyPacket(const void *body, int body_size) {
+ auto packet = this->MakeSendPacketCommon(0);
+ if (packet && packet->IsAllocationSucceeded()) {
+ packet->GetHeader()->packet_type = HtcctrlPacketType_ReadyFromTarget;
+
+ std::memcpy(packet->GetBody(), body, packet->GetBodySize());
+ }
+
+ return packet;
+ }
+
+ std::unique_ptr HtcctrlPacketFactory::MakeInformationPacket(const void *body, int body_size) {
+ auto packet = this->MakeSendPacketCommon(0);
+ if (packet && packet->IsAllocationSucceeded()) {
+ packet->GetHeader()->packet_type = HtcctrlPacketType_InformationFromTarget;
+
+ std::memcpy(packet->GetBody(), body, packet->GetBodySize());
+ }
+
+ return packet;
+ }
+
+ std::unique_ptr HtcctrlPacketFactory::MakeDisconnectPacket() {
+ auto packet = this->MakeSendPacketCommon(0);
+ if (packet) {
+ packet->GetHeader()->packet_type = HtcctrlPacketType_DisconnectFromTarget;
+ }
+
+ return packet;
+ }
+
+ std::unique_ptr HtcctrlPacketFactory::MakeConnectPacket(const void *body, int body_size) {
+ auto packet = this->MakeSendPacketCommon(0);
+ if (packet && packet->IsAllocationSucceeded()) {
+ packet->GetHeader()->packet_type = HtcctrlPacketType_ConnectFromTarget;
+
+ std::memcpy(packet->GetBody(), body, packet->GetBodySize());
+ }
+
+ return packet;
+ }
+
+ std::unique_ptr HtcctrlPacketFactory::MakeBeaconResponsePacket(const void *body, int body_size) {
+ auto packet = this->MakeSendPacketCommon(0);
+ if (packet && packet->IsAllocationSucceeded()) {
+ packet->GetHeader()->packet_type = HtcctrlPacketType_BeaconResponse;
+
+ std::memcpy(packet->GetBody(), body, packet->GetBodySize());
+ }
+
+ return packet;
+ }
+
+ void HtcctrlPacketFactory::Delete(HtcctrlPacket *packet) {
+ HtcctrlPacketDeleter{m_allocator}(packet);
+ }
+
+}
diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp
index 8670672d7..5ed6834d6 100644
--- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp
+++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_packet_factory.hpp
@@ -15,26 +15,39 @@
*/
#pragma once
#include
+#include "htclow_ctrl_packet.hpp"
namespace ams::htclow::ctrl {
class HtcctrlPacketFactory {
private:
mem::StandardAllocator *m_allocator;
- u32 m_seed;
+ u32 m_sequence_id;
public:
HtcctrlPacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) {
/* Get the current time. */
const u64 time = os::GetSystemTick().GetInt64Value();
- /* Set the random seed. */
+ /* Set a random sequence id. */
{
util::TinyMT rng;
rng.Initialize(reinterpret_cast(std::addressof(time)), sizeof(time) / sizeof(u32));
- m_seed = rng.GenerateRandomU32();
+ m_sequence_id = rng.GenerateRandomU32();
}
}
+ public:
+ std::unique_ptr MakeSuspendPacket();
+ std::unique_ptr MakeResumePacket();
+ std::unique_ptr MakeReadyPacket(const void *body, int body_size);
+ std::unique_ptr MakeInformationPacket(const void *body, int body_size);
+ std::unique_ptr MakeDisconnectPacket();
+ std::unique_ptr MakeConnectPacket(const void *body, int body_size);
+ std::unique_ptr MakeBeaconResponsePacket(const void *body, int body_size);
+
+ void Delete(HtcctrlPacket *packet);
+ private:
+ std::unique_ptr MakeSendPacketCommon(int body_size);
};
}
diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.cpp
new file mode 100644
index 000000000..77de070c7
--- /dev/null
+++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "htclow_ctrl_send_buffer.hpp"
+
+namespace ams::htclow::ctrl {
+
+ bool HtcctrlSendBuffer::IsPriorPacket(HtcctrlPacketType packet_type) const {
+ return packet_type == HtcctrlPacketType_DisconnectFromTarget;
+ }
+
+ bool HtcctrlSendBuffer::IsPosteriorPacket(HtcctrlPacketType packet_type) const {
+ switch (packet_type) {
+ case HtcctrlPacketType_ConnectFromTarget:
+ case HtcctrlPacketType_ReadyFromTarget:
+ case HtcctrlPacketType_SuspendFromTarget:
+ case HtcctrlPacketType_ResumeFromTarget:
+ case HtcctrlPacketType_BeaconResponse:
+ case HtcctrlPacketType_InformationFromTarget:
+ default:
+ return false;
+ }
+ }
+
+ void HtcctrlSendBuffer::AddPacket(std::unique_ptr ptr) {
+ /* Get the packet. */
+ HtcctrlPacket *packet = ptr.release();
+
+ /* Get the packet type. */
+ const auto packet_type = packet->GetHeader()->packet_type;
+
+ /* Add the packet to the appropriate list. */
+ if (this->IsPriorPacket(packet_type)) {
+ m_prior_packet_list.push_back(*packet);
+ } else {
+ AMS_ABORT_UNLESS(this->IsPosteriorPacket(packet_type));
+ m_posterior_packet_list.push_back(*packet);
+ }
+ }
+
+}
diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp
index abe1cf239..d5c8c4798 100644
--- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp
+++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_send_buffer.hpp
@@ -26,9 +26,15 @@ namespace ams::htclow::ctrl {
using PacketList = util::IntrusiveListBaseTraits::ListType;
private:
HtcctrlPacketFactory *m_packet_factory;
- PacketList m_packet_list;
+ PacketList m_prior_packet_list;
+ PacketList m_posterior_packet_list;
+ private:
+ bool IsPriorPacket(HtcctrlPacketType packet_type) const;
+ bool IsPosteriorPacket(HtcctrlPacketType packet_type) const;
public:
- HtcctrlSendBuffer(HtcctrlPacketFactory *pf) : m_packet_factory(pf), m_packet_list() { /* ... */ }
+ HtcctrlSendBuffer(HtcctrlPacketFactory *pf) : m_packet_factory(pf), m_prior_packet_list(), m_posterior_packet_list() { /* ... */ }
+
+ void AddPacket(std::unique_ptr ptr);
};
}
diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp
index 55bb00d02..e1fd3b7cc 100644
--- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp
+++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp
@@ -17,6 +17,9 @@
#include "htclow_ctrl_service.hpp"
#include "htclow_ctrl_state.hpp"
#include "htclow_ctrl_state_machine.hpp"
+#include "htclow_ctrl_packet_factory.hpp"
+#include "htclow_service_channel_parser.hpp"
+#include "htclow_ctrl_service_channels.hpp"
#include "../mux/htclow_mux.hpp"
namespace ams::htclow::ctrl {
@@ -38,7 +41,7 @@ namespace ams::htclow::ctrl {
HtcctrlService::HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux)
: m_settings_holder(), m_beacon_response(), m_1100(), m_packet_factory(pf), m_state_machine(sm), m_mux(mux), m_event(os::EventClearMode_ManualClear),
- m_send_buffer(pf), m_mutex(), m_condvar(), m_2170(), m_version(ProtocolVersion)
+ m_send_buffer(pf), m_mutex(), m_condvar(), m_service_channels_packet(), m_version(ProtocolVersion)
{
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
@@ -110,8 +113,150 @@ namespace ams::htclow::ctrl {
}
Result HtcctrlService::ProcessReceivePacket(const HtcctrlPacketHeader &header, const void *body, size_t body_size) {
- /* TODO */
- AMS_ABORT("HtcctrlService::ProcessReceivePacket");
+ /* Lock ourselves. */
+ std::scoped_lock lk(m_mutex);
+
+ switch (header.packet_type) {
+ case HtcctrlPacketType_ConnectFromHost:
+ return this->ProcessReceiveConnectPacket();
+ case HtcctrlPacketType_ReadyFromHost:
+ return this->ProcessReceiveReadyPacket(body, body_size);
+ case HtcctrlPacketType_SuspendFromHost:
+ return this->ProcessReceiveSuspendPacket();
+ case HtcctrlPacketType_ResumeFromHost:
+ return this->ProcessReceiveResumePacket();
+ case HtcctrlPacketType_DisconnectFromHost:
+ return this->ProcessReceiveDisconnectPacket();
+ case HtcctrlPacketType_BeaconQuery:
+ return this->ProcessReceiveBeaconQueryPacket();
+ default:
+ return this->ProcessReceiveUnexpectedPacket();
+ }
+ }
+
+ Result HtcctrlService::ProcessReceiveConnectPacket() {
+ /* Try to transition to sent connect state. */
+ if (R_FAILED(this->SetState(HtcctrlState_SentConnectFromHost))) {
+ /* We couldn't transition to sent connect. */
+ return this->ProcessReceiveUnexpectedPacket();
+ }
+
+ /* Send a connect packet. */
+ m_send_buffer.AddPacket(m_packet_factory->MakeConnectPacket(m_beacon_response, util::Strnlen(m_beacon_response, sizeof(m_beacon_response)) + 1));
+
+ /* Signal our event. */
+ m_event.Signal();
+
+ return ResultSuccess();
+ }
+
+ Result HtcctrlService::ProcessReceiveReadyPacket(const void *body, size_t body_size) {
+ /* Update our service channels. */
+ this->UpdateServiceChannels(body, body_size);
+
+ /* Check that our version is correct. */
+ if (m_version < ProtocolVersion) {
+ return this->ProcessReceiveUnexpectedPacket();
+ }
+
+ /* Set our version. */
+ m_version = ProtocolVersion;
+ m_mux->SetVersion(m_version);
+
+ /* Set our state. */
+ if (R_FAILED(this->SetState(HtcctrlState_SentReadyFromHost))) {
+ return this->ProcessReceiveUnexpectedPacket();
+ }
+
+ /* Ready ourselves. */
+ this->TryReadyInternal();
+ return ResultSuccess();
+ }
+
+ Result HtcctrlService::ProcessReceiveSuspendPacket() {
+ /* Try to set our state to enter sleep. */
+ if (R_FAILED(this->SetState(HtcctrlState_EnterSleep))) {
+ /* We couldn't transition to sleep. */
+ return this->ProcessReceiveUnexpectedPacket();
+ }
+
+ return ResultSuccess();
+ }
+
+ Result HtcctrlService::ProcessReceiveResumePacket() {
+ /* If our state is sent-resume, change to readied. */
+ if (m_state_machine->GetHtcctrlState() != HtcctrlState_SentResumeFromTarget || R_FAILED(this->SetState(HtcctrlState_Ready))) {
+ /* We couldn't perform a valid resume transition. */
+ return this->ProcessReceiveUnexpectedPacket();
+ }
+
+ return ResultSuccess();
+ }
+
+ Result HtcctrlService::ProcessReceiveDisconnectPacket() {
+ /* Set our state. */
+ R_TRY(this->SetState(HtcctrlState_Disconnected));
+
+ return ResultSuccess();
+ }
+
+ Result HtcctrlService::ProcessReceiveBeaconQueryPacket() {
+ /* Send a beacon response packet. */
+ m_send_buffer.AddPacket(m_packet_factory->MakeBeaconResponsePacket(m_beacon_response, util::Strnlen(m_beacon_response, sizeof(m_beacon_response)) + 1));
+
+ /* Signal our event. */
+ m_event.Signal();
+
+ return ResultSuccess();
+ }
+
+ Result HtcctrlService::ProcessReceiveUnexpectedPacket() {
+ /* Set our state. */
+ R_TRY(this->SetState(HtcctrlState_Error));
+
+ /* Send a disconnection packet. */
+ m_send_buffer.AddPacket(m_packet_factory->MakeDisconnectPacket());
+
+ /* Signal our event. */
+ m_event.Signal();
+
+ /* Return unexpected packet error. */
+ return htclow::ResultHtcctrlReceiveUnexpectedPacket();
+ }
+
+ void HtcctrlService::UpdateServiceChannels(const void *body, size_t body_size) {
+ /* Copy the packet body to our member. */
+ std::memcpy(m_service_channels_packet, body, body_size);
+
+ /* Parse service channels. */
+ impl::ChannelInternalType channels[10];
+ int num_channels;
+ s16 version = m_version;
+ ctrl::ParseServiceChannel(std::addressof(version), channels, std::addressof(num_channels), util::size(channels), m_service_channels_packet, body_size);
+
+ /* Update version. */
+ m_version = version;
+
+ /* Notify state machine of supported channels. */
+ m_state_machine->NotifySupportedServiceChannels(channels, num_channels);
+ }
+
+ void HtcctrlService::TryReadyInternal() {
+ /* If we can send ready, do so. */
+ if (m_state_machine->IsPossibleToSendReady()) {
+ /* Print the channels. */
+ char channel_str[0x100];
+ this->PrintServiceChannels(channel_str, sizeof(channel_str));
+
+ /* Send a ready packet. */
+ m_send_buffer.AddPacket(m_packet_factory->MakeReadyPacket(channel_str, util::Strnlen(channel_str, sizeof(channel_str)) + 1));
+
+ /* Signal our event. */
+ m_event.Signal();
+
+ /* Set connecting checked in state machine. */
+ m_state_machine->SetConnectingChecked();
+ }
}
Result HtcctrlService::NotifyDriverConnected() {
@@ -168,4 +313,13 @@ namespace ams::htclow::ctrl {
m_condvar.Broadcast();
}
+ void HtcctrlService::PrintServiceChannels(char *dst, size_t dst_size) {
+ size_t ofs = 0;
+ ofs += util::SNPrintf(dst + ofs, dst_size - ofs, "{\r\n \"Chan\" : [\r\n \"%d:%d:%d\"", static_cast(ServiceChannels[0].module_id), ServiceChannels[0].reserved, static_cast(ServiceChannels[0].channel_id));
+ for (size_t i = 1; i < util::size(ServiceChannels); ++i) {
+ ofs += util::SNPrintf(dst + ofs, dst_size - ofs, ",\r\n \"%d:%d:%d\"", static_cast(ServiceChannels[i].module_id), ServiceChannels[i].reserved, static_cast(ServiceChannels[i].channel_id));
+ }
+ ofs += util::SNPrintf(dst + ofs, dst_size - ofs, "\r\n],\r\n \"Prot\" : %d\r\n}\r\n", ProtocolVersion);
+ }
+
}
diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp
index b39fb6f41..b94dc58a8 100644
--- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp
+++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp
@@ -50,13 +50,26 @@ namespace ams::htclow::ctrl {
HtcctrlSendBuffer m_send_buffer;
os::SdkMutex m_mutex;
os::SdkConditionVariable m_condvar;
- u8 m_2170[0x1000];
+ char m_service_channels_packet[0x1000];
s16 m_version;
private:
const char *GetConnectionType(impl::DriverType driver_type) const;
void UpdateBeaconResponse(const char *connection);
+ Result ProcessReceiveConnectPacket();
+ Result ProcessReceiveReadyPacket(const void *body, size_t body_size);
+ Result ProcessReceiveSuspendPacket();
+ Result ProcessReceiveResumePacket();
+ Result ProcessReceiveDisconnectPacket();
+ Result ProcessReceiveBeaconQueryPacket();
+ Result ProcessReceiveUnexpectedPacket();
+
+ void UpdateServiceChannels(const void *body, size_t body_size);
+ void TryReadyInternal();
+
+ void PrintServiceChannels(char *dst, size_t dst_size);
+
Result SetState(HtcctrlState state);
void ReflectState();
public:
diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp
new file mode 100644
index 000000000..bcaf32b9f
--- /dev/null
+++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service_channels.hpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::htclow::ctrl {
+
+ constexpr inline const impl::ChannelInternalType ServiceChannels[] = {
+ {
+ .channel_id = 0,
+ .module_id = static_cast(1),
+ },
+ {
+ .channel_id = 1,
+ .module_id = static_cast(3),
+ },
+ {
+ .channel_id = 2,
+ .module_id = static_cast(3),
+ },
+ {
+ .channel_id = 0,
+ .module_id = static_cast(4),
+ },
+ };
+
+}
diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp
index c80bd784b..1836dbeeb 100644
--- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp
+++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.cpp
@@ -15,33 +15,10 @@
*/
#include
#include "htclow_ctrl_state_machine.hpp"
+#include "htclow_ctrl_service_channels.hpp"
namespace ams::htclow::ctrl {
- namespace {
-
- /* TODO: Real module id names */
- constexpr const impl::ChannelInternalType ServiceChannels[] = {
- {
- .channel_id = 0,
- .module_id = static_cast(1),
- },
- {
- .channel_id = 1,
- .module_id = static_cast(3),
- },
- {
- .channel_id = 2,
- .module_id = static_cast(3),
- },
- {
- .channel_id = 0,
- .module_id = static_cast(4),
- },
- };
-
- }
-
HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_DriverDisconnected), m_prev_state(HtcctrlState_DriverDisconnected), m_mutex() {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
@@ -67,7 +44,7 @@ namespace ams::htclow::ctrl {
std::scoped_lock lk(m_mutex);
/* Check that the transition is allowed. */
- R_UNLESS(ctrl::IsStateTransitionAllowed(m_state, state), htclow::ResultStateTransitionNotAllowed());
+ R_UNLESS(ctrl::IsStateTransitionAllowed(m_state, state), htclow::ResultHtcctrlStateTransitionNotAllowed());
/* Get the state pre-transition. */
const auto old_state = m_state;
@@ -149,6 +126,13 @@ namespace ams::htclow::ctrl {
}
}
+ bool HtcctrlStateMachine::IsPossibleToSendReady() {
+ /* Lock ourselves. */
+ std::scoped_lock lk(m_mutex);
+
+ return m_state == HtcctrlState_SentReadyFromHost && this->AreServiceChannelsConnecting();
+ }
+
bool HtcctrlStateMachine::IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
@@ -175,4 +159,46 @@ namespace ams::htclow::ctrl {
}
}
+ void HtcctrlStateMachine::SetConnectingChecked() {
+ /* Lock ourselves. */
+ std::scoped_lock lk(m_mutex);
+
+ for (auto &pair : m_map) {
+ pair.second.connect = ServiceChannelConnect_ConnectingChecked;
+ }
+ }
+
+ void HtcctrlStateMachine::NotifySupportedServiceChannels(const impl::ChannelInternalType *channels, int num_channels) {
+ /* Lock ourselves. */
+ std::scoped_lock lk(m_mutex);
+
+ auto IsSupportedServiceChannel = [] ALWAYS_INLINE_LAMBDA (const impl::ChannelInternalType &channel, const impl::ChannelInternalType *supported, int num_supported) -> bool {
+ for (auto i = 0; i < num_supported; ++i) {
+ if (channel.module_id == supported[i].module_id && channel.channel_id == supported[i].channel_id) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ for (auto &pair : m_map) {
+ if (IsSupportedServiceChannel(pair.first, channels, num_channels)) {
+ pair.second.support = ServiceChannelSupport_Suppported;
+ } else {
+ pair.second.support = ServiceChannelSupport_Unsupported;
+ }
+ }
+ }
+
+ bool HtcctrlStateMachine::AreServiceChannelsConnecting() {
+ for (auto &pair : m_map) {
+ if (pair.second.connect != ServiceChannelConnect_Connecting) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
}
diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp
index 6651eff6e..aed8d5068 100644
--- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp
+++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_state_machine.hpp
@@ -64,13 +64,19 @@ namespace ams::htclow::ctrl {
bool IsDisconnected();
bool IsSleeping();
+ bool IsPossibleToSendReady();
bool IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel);
bool IsConnectable(const impl::ChannelInternalType &channel);
void SetNotConnecting(const impl::ChannelInternalType &channel);
+ void SetConnectingChecked();
+
+ void NotifySupportedServiceChannels(const impl::ChannelInternalType *channels, int num_channels);
private:
void SetStateWithoutCheckInternal(HtcctrlState state);
+ bool AreServiceChannelsConnecting();
+
void ClearServiceChannelStates();
};
diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp
index 383d343c4..30e976b20 100644
--- a/libraries/libvapours/include/vapours/results/htclow_results.hpp
+++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp
@@ -49,6 +49,8 @@ namespace ams::htclow {
R_DEFINE_ERROR_RESULT(UsbDriverReceiveError, 1403);
R_DEFINE_ERROR_RESULT(UsbDriverSendError, 1404);
- R_DEFINE_ERROR_RESULT(StateTransitionNotAllowed, 2001);
+ R_DEFINE_ERROR_RESULT(HtcctrlError, 2000); /* TODO: Range? */
+ R_DEFINE_ERROR_RESULT(HtcctrlStateTransitionNotAllowed, 2001);
+ R_DEFINE_ERROR_RESULT(HtcctrlReceiveUnexpectedPacket, 2002);
}