mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-02-09 19:25:10 +01:00
htc: add remaining worker receive thread logic
This commit is contained in:
parent
6fc24d8883
commit
2cdfde6637
@ -39,7 +39,7 @@ namespace ams::htclow::ctrl {
|
|||||||
|
|
||||||
struct HtcctrlPacketHeader {
|
struct HtcctrlPacketHeader {
|
||||||
u32 signature;
|
u32 signature;
|
||||||
u32 offset;
|
u32 sequence_id;
|
||||||
u32 reserved;
|
u32 reserved;
|
||||||
u32 body_size;
|
u32 body_size;
|
||||||
s16 version;
|
s16 version;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -15,26 +15,39 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
#include "htclow_ctrl_packet.hpp"
|
||||||
|
|
||||||
namespace ams::htclow::ctrl {
|
namespace ams::htclow::ctrl {
|
||||||
|
|
||||||
class HtcctrlPacketFactory {
|
class HtcctrlPacketFactory {
|
||||||
private:
|
private:
|
||||||
mem::StandardAllocator *m_allocator;
|
mem::StandardAllocator *m_allocator;
|
||||||
u32 m_seed;
|
u32 m_sequence_id;
|
||||||
public:
|
public:
|
||||||
HtcctrlPacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) {
|
HtcctrlPacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) {
|
||||||
/* Get the current time. */
|
/* Get the current time. */
|
||||||
const u64 time = os::GetSystemTick().GetInt64Value();
|
const u64 time = os::GetSystemTick().GetInt64Value();
|
||||||
|
|
||||||
/* Set the random seed. */
|
/* Set a random sequence id. */
|
||||||
{
|
{
|
||||||
util::TinyMT rng;
|
util::TinyMT rng;
|
||||||
rng.Initialize(reinterpret_cast<const u32 *>(std::addressof(time)), sizeof(time) / sizeof(u32));
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -26,9 +26,15 @@ namespace ams::htclow::ctrl {
|
|||||||
using PacketList = util::IntrusiveListBaseTraits<HtcctrlPacket>::ListType;
|
using PacketList = util::IntrusiveListBaseTraits<HtcctrlPacket>::ListType;
|
||||||
private:
|
private:
|
||||||
HtcctrlPacketFactory *m_packet_factory;
|
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:
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
#include "htclow_ctrl_service.hpp"
|
#include "htclow_ctrl_service.hpp"
|
||||||
#include "htclow_ctrl_state.hpp"
|
#include "htclow_ctrl_state.hpp"
|
||||||
#include "htclow_ctrl_state_machine.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"
|
#include "../mux/htclow_mux.hpp"
|
||||||
|
|
||||||
namespace ams::htclow::ctrl {
|
namespace ams::htclow::ctrl {
|
||||||
@ -38,7 +41,7 @@ namespace ams::htclow::ctrl {
|
|||||||
|
|
||||||
HtcctrlService::HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux)
|
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_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. */
|
/* Lock ourselves. */
|
||||||
std::scoped_lock lk(m_mutex);
|
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) {
|
Result HtcctrlService::ProcessReceivePacket(const HtcctrlPacketHeader &header, const void *body, size_t body_size) {
|
||||||
/* TODO */
|
/* Lock ourselves. */
|
||||||
AMS_ABORT("HtcctrlService::ProcessReceivePacket");
|
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() {
|
Result HtcctrlService::NotifyDriverConnected() {
|
||||||
@ -168,4 +313,13 @@ namespace ams::htclow::ctrl {
|
|||||||
m_condvar.Broadcast();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -50,13 +50,26 @@ namespace ams::htclow::ctrl {
|
|||||||
HtcctrlSendBuffer m_send_buffer;
|
HtcctrlSendBuffer m_send_buffer;
|
||||||
os::SdkMutex m_mutex;
|
os::SdkMutex m_mutex;
|
||||||
os::SdkConditionVariable m_condvar;
|
os::SdkConditionVariable m_condvar;
|
||||||
u8 m_2170[0x1000];
|
char m_service_channels_packet[0x1000];
|
||||||
s16 m_version;
|
s16 m_version;
|
||||||
private:
|
private:
|
||||||
const char *GetConnectionType(impl::DriverType driver_type) const;
|
const char *GetConnectionType(impl::DriverType driver_type) const;
|
||||||
|
|
||||||
void UpdateBeaconResponse(const char *connection);
|
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);
|
Result SetState(HtcctrlState state);
|
||||||
void ReflectState();
|
void ReflectState();
|
||||||
public:
|
public:
|
||||||
|
@ -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),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -15,33 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "htclow_ctrl_state_machine.hpp"
|
#include "htclow_ctrl_state_machine.hpp"
|
||||||
|
#include "htclow_ctrl_service_channels.hpp"
|
||||||
|
|
||||||
namespace ams::htclow::ctrl {
|
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() {
|
HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_DriverDisconnected), m_prev_state(HtcctrlState_DriverDisconnected), m_mutex() {
|
||||||
/* Lock ourselves. */
|
/* Lock ourselves. */
|
||||||
std::scoped_lock lk(m_mutex);
|
std::scoped_lock lk(m_mutex);
|
||||||
@ -67,7 +44,7 @@ namespace ams::htclow::ctrl {
|
|||||||
std::scoped_lock lk(m_mutex);
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
/* Check that the transition is allowed. */
|
/* 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. */
|
/* Get the state pre-transition. */
|
||||||
const auto old_state = m_state;
|
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) {
|
bool HtcctrlStateMachine::IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel) {
|
||||||
/* Lock ourselves. */
|
/* Lock ourselves. */
|
||||||
std::scoped_lock lk(m_mutex);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -64,13 +64,19 @@ namespace ams::htclow::ctrl {
|
|||||||
bool IsDisconnected();
|
bool IsDisconnected();
|
||||||
bool IsSleeping();
|
bool IsSleeping();
|
||||||
|
|
||||||
|
bool IsPossibleToSendReady();
|
||||||
bool IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel);
|
bool IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel);
|
||||||
bool IsConnectable(const impl::ChannelInternalType &channel);
|
bool IsConnectable(const impl::ChannelInternalType &channel);
|
||||||
|
|
||||||
void SetNotConnecting(const impl::ChannelInternalType &channel);
|
void SetNotConnecting(const impl::ChannelInternalType &channel);
|
||||||
|
void SetConnectingChecked();
|
||||||
|
|
||||||
|
void NotifySupportedServiceChannels(const impl::ChannelInternalType *channels, int num_channels);
|
||||||
private:
|
private:
|
||||||
void SetStateWithoutCheckInternal(HtcctrlState state);
|
void SetStateWithoutCheckInternal(HtcctrlState state);
|
||||||
|
|
||||||
|
bool AreServiceChannelsConnecting();
|
||||||
|
|
||||||
void ClearServiceChannelStates();
|
void ClearServiceChannelStates();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,6 +49,8 @@ namespace ams::htclow {
|
|||||||
R_DEFINE_ERROR_RESULT(UsbDriverReceiveError, 1403);
|
R_DEFINE_ERROR_RESULT(UsbDriverReceiveError, 1403);
|
||||||
R_DEFINE_ERROR_RESULT(UsbDriverSendError, 1404);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user