1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-12-23 02:42:09 +00:00

htc: add remaining worker receive thread logic

This commit is contained in:
Michael Scire 2021-02-08 23:44:30 -08:00 committed by SciresM
parent 6fc24d8883
commit 2cdfde6637
11 changed files with 477 additions and 36 deletions

View file

@ -39,7 +39,7 @@ namespace ams::htclow::ctrl {
struct HtcctrlPacketHeader {
u32 signature;
u32 offset;
u32 sequence_id;
u32 reserved;
u32 body_size;
s16 version;

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "htclow_ctrl_packet_factory.hpp"
namespace ams::htclow::ctrl {
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> 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<HtcctrlPacket *>(buffer);
/* Construct the packet. */
std::construct_at(packet, m_allocator, body_size + sizeof(HtcctrlPacketHeader));
/* Create the unique pointer. */
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> 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<HtcctrlPacket, HtcctrlPacketDeleter>(nullptr, HtcctrlPacketDeleter{m_allocator});
}
}
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> HtcctrlPacketFactory::MakeSuspendPacket() {
auto packet = this->MakeSendPacketCommon(0);
if (packet && packet->IsAllocationSucceeded()) {
packet->GetHeader()->packet_type = HtcctrlPacketType_SuspendFromTarget;
}
return packet;
}
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> HtcctrlPacketFactory::MakeResumePacket() {
auto packet = this->MakeSendPacketCommon(0);
if (packet && packet->IsAllocationSucceeded()) {
packet->GetHeader()->packet_type = HtcctrlPacketType_SuspendFromTarget;
}
return packet;
}
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> 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<HtcctrlPacket, HtcctrlPacketDeleter> 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<HtcctrlPacket, HtcctrlPacketDeleter> HtcctrlPacketFactory::MakeDisconnectPacket() {
auto packet = this->MakeSendPacketCommon(0);
if (packet) {
packet->GetHeader()->packet_type = HtcctrlPacketType_DisconnectFromTarget;
}
return packet;
}
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> 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<HtcctrlPacket, HtcctrlPacketDeleter> 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);
}
}

View file

@ -15,26 +15,39 @@
*/
#pragma once
#include <stratosphere.hpp>
#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<const u32 *>(std::addressof(time)), sizeof(time) / sizeof(u32));
m_seed = rng.GenerateRandomU32();
m_sequence_id = rng.GenerateRandomU32();
}
}
public:
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> MakeSuspendPacket();
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> MakeResumePacket();
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> MakeReadyPacket(const void *body, int body_size);
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> MakeInformationPacket(const void *body, int body_size);
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> MakeDisconnectPacket();
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> MakeConnectPacket(const void *body, int body_size);
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> MakeBeaconResponsePacket(const void *body, int body_size);
void Delete(HtcctrlPacket *packet);
private:
std::unique_ptr<HtcctrlPacket, HtcctrlPacketDeleter> MakeSendPacketCommon(int body_size);
};
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<HtcctrlPacket, HtcctrlPacketDeleter> 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);
}
}
}

View file

@ -26,9 +26,15 @@ namespace ams::htclow::ctrl {
using PacketList = util::IntrusiveListBaseTraits<HtcctrlPacket>::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<HtcctrlPacket, HtcctrlPacketDeleter> ptr);
};
}

View file

@ -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<int>(ServiceChannels[0].module_id), ServiceChannels[0].reserved, static_cast<int>(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<int>(ServiceChannels[i].module_id), ServiceChannels[i].reserved, static_cast<int>(ServiceChannels[i].channel_id));
}
ofs += util::SNPrintf(dst + ofs, dst_size - ofs, "\r\n],\r\n \"Prot\" : %d\r\n}\r\n", ProtocolVersion);
}
}

View file

@ -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:

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::htclow::ctrl {
constexpr inline const impl::ChannelInternalType ServiceChannels[] = {
{
.channel_id = 0,
.module_id = static_cast<ModuleId>(1),
},
{
.channel_id = 1,
.module_id = static_cast<ModuleId>(3),
},
{
.channel_id = 2,
.module_id = static_cast<ModuleId>(3),
},
{
.channel_id = 0,
.module_id = static_cast<ModuleId>(4),
},
};
}

View file

@ -15,33 +15,10 @@
*/
#include <stratosphere.hpp>
#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<ModuleId>(1),
},
{
.channel_id = 1,
.module_id = static_cast<ModuleId>(3),
},
{
.channel_id = 2,
.module_id = static_cast<ModuleId>(3),
},
{
.channel_id = 0,
.module_id = static_cast<ModuleId>(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;
}
}

View file

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

View file

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