mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-28 09:30:58 +01:00
htc: implement much of worker receive logic
This commit is contained in:
parent
8f85cc17dc
commit
679fec2ddc
@ -34,6 +34,10 @@ namespace ams::htclow {
|
|||||||
ChannelState_Disconnected = 3,
|
ChannelState_Disconnected = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ChannelConfig {
|
||||||
|
bool flow_control_enabled;
|
||||||
|
};
|
||||||
|
|
||||||
constexpr bool IsStateTransitionAllowed(ChannelState from, ChannelState to) {
|
constexpr bool IsStateTransitionAllowed(ChannelState from, ChannelState to) {
|
||||||
switch (from) {
|
switch (from) {
|
||||||
case ChannelState_Connectable:
|
case ChannelState_Connectable:
|
||||||
|
@ -15,11 +15,59 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
#include "../htclow_packet.hpp"
|
||||||
|
|
||||||
namespace ams::htclow::ctrl {
|
namespace ams::htclow::ctrl {
|
||||||
|
|
||||||
class HtcctrlPacket : public util::IntrusiveListBaseNode<HtcctrlPacket> {
|
enum HtcctrlPacketType : u16 {
|
||||||
/* TODO */
|
HtcctrlPacketType_ConnectFromHost = 16,
|
||||||
|
HtcctrlPacketType_ConnectFromTarget = 17,
|
||||||
|
HtcctrlPacketType_ReadyFromHost = 18,
|
||||||
|
HtcctrlPacketType_ReadyFromTarget = 19,
|
||||||
|
HtcctrlPacketType_SuspendFromHost = 20,
|
||||||
|
HtcctrlPacketType_SuspendFromTarget = 21,
|
||||||
|
HtcctrlPacketType_ResumeFromHost = 22,
|
||||||
|
HtcctrlPacketType_ResumeFromTarget = 23,
|
||||||
|
HtcctrlPacketType_DisconnectFromHost = 24,
|
||||||
|
HtcctrlPacketType_DisconnectFromTarget = 25,
|
||||||
|
HtcctrlPacketType_BeaconQuery = 28,
|
||||||
|
HtcctrlPacketType_BeaconResponse = 29,
|
||||||
|
HtcctrlPacketType_InformationFromTarget = 33,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr inline u32 HtcctrlSignature = 0x78825637;
|
||||||
|
|
||||||
|
struct HtcctrlPacketHeader {
|
||||||
|
u32 signature;
|
||||||
|
u32 offset;
|
||||||
|
u32 reserved;
|
||||||
|
u32 body_size;
|
||||||
|
s16 version;
|
||||||
|
HtcctrlPacketType packet_type;
|
||||||
|
impl::ChannelInternalType channel;
|
||||||
|
u64 share;
|
||||||
|
};
|
||||||
|
static_assert(util::is_pod<HtcctrlPacketHeader>::value);
|
||||||
|
static_assert(sizeof(HtcctrlPacketHeader) == 0x20);
|
||||||
|
|
||||||
|
static constexpr inline size_t HtcctrlPacketBodySizeMax = 0x1000;
|
||||||
|
|
||||||
|
struct HtcctrlPacketBody {
|
||||||
|
u8 data[HtcctrlPacketBodySizeMax];
|
||||||
|
};
|
||||||
|
|
||||||
|
class HtcctrlPacket : public BasePacket<HtcctrlPacketHeader>, public util::IntrusiveListBaseNode<HtcctrlPacket> {
|
||||||
|
public:
|
||||||
|
using BasePacket<HtcctrlPacketHeader>::BasePacket;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HtcctrlPacketDeleter {
|
||||||
|
mem::StandardAllocator *m_allocator;
|
||||||
|
|
||||||
|
void operator()(HtcctrlPacket *packet) {
|
||||||
|
std::destroy_at(packet);
|
||||||
|
m_allocator->Free(packet);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,37 @@ namespace ams::htclow::ctrl {
|
|||||||
this->UpdateBeaconResponse(this->GetConnectionType(driver_type));
|
this->UpdateBeaconResponse(this->GetConnectionType(driver_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result HtcctrlService::CheckReceivedHeader(const HtcctrlPacketHeader &header) const {
|
||||||
|
/* Check the packet signature. */
|
||||||
|
AMS_ASSERT(header.signature == HtcctrlSignature);
|
||||||
|
|
||||||
|
/* Validate version. */
|
||||||
|
R_UNLESS(header.version == 1, htclow::ResultProtocolError());
|
||||||
|
|
||||||
|
/* Switch on the packet type. */
|
||||||
|
switch (header.packet_type) {
|
||||||
|
case HtcctrlPacketType_ConnectFromHost:
|
||||||
|
case HtcctrlPacketType_SuspendFromHost:
|
||||||
|
case HtcctrlPacketType_ResumeFromHost:
|
||||||
|
case HtcctrlPacketType_DisconnectFromHost:
|
||||||
|
case HtcctrlPacketType_BeaconQuery:
|
||||||
|
R_UNLESS(header.body_size == 0, htclow::ResultProtocolError());
|
||||||
|
break;
|
||||||
|
case HtcctrlPacketType_ReadyFromHost:
|
||||||
|
R_UNLESS(0 <= header.body_size && header.body_size <= sizeof(HtcctrlPacketBody), htclow::ResultProtocolError());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return htclow::ResultProtocolError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result HtcctrlService::ProcessReceivePacket(const HtcctrlPacketHeader &header, const void *body, size_t body_size) {
|
||||||
|
/* TODO */
|
||||||
|
AMS_ABORT("HtcctrlService::ProcessReceivePacket");
|
||||||
|
}
|
||||||
|
|
||||||
Result HtcctrlService::NotifyDriverConnected() {
|
Result HtcctrlService::NotifyDriverConnected() {
|
||||||
/* Lock ourselves. */
|
/* Lock ourselves. */
|
||||||
std::scoped_lock lk(m_mutex);
|
std::scoped_lock lk(m_mutex);
|
||||||
|
@ -64,6 +64,9 @@ namespace ams::htclow::ctrl {
|
|||||||
|
|
||||||
void SetDriverType(impl::DriverType driver_type);
|
void SetDriverType(impl::DriverType driver_type);
|
||||||
|
|
||||||
|
Result CheckReceivedHeader(const HtcctrlPacketHeader &header) const;
|
||||||
|
Result ProcessReceivePacket(const HtcctrlPacketHeader &header, const void *body, size_t body_size);
|
||||||
|
|
||||||
Result NotifyDriverConnected();
|
Result NotifyDriverConnected();
|
||||||
Result NotifyDriverDisconnected();
|
Result NotifyDriverDisconnected();
|
||||||
};
|
};
|
||||||
|
@ -18,10 +18,86 @@
|
|||||||
|
|
||||||
namespace ams::htclow {
|
namespace ams::htclow {
|
||||||
|
|
||||||
class Packet : public util::IntrusiveListBaseNode<Packet> {
|
enum PacketType : u16 {
|
||||||
/* TODO */
|
PacketType_Data = 24,
|
||||||
|
PacketType_MaxData = 25,
|
||||||
|
PacketType_Error = 26,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr inline u32 HtcGen2Signature = 0xA79F3540;
|
||||||
|
|
||||||
|
struct PacketHeader {
|
||||||
|
u32 signature;
|
||||||
|
u32 offset;
|
||||||
|
u32 reserved;
|
||||||
|
u32 body_size;
|
||||||
|
s16 version;
|
||||||
|
PacketType packet_type;
|
||||||
|
impl::ChannelInternalType channel;
|
||||||
|
u64 share;
|
||||||
|
};
|
||||||
|
static_assert(util::is_pod<PacketHeader>::value);
|
||||||
|
static_assert(sizeof(PacketHeader) == 0x20);
|
||||||
|
|
||||||
|
static constexpr inline size_t PacketBodySizeMax = 0x3E000;
|
||||||
|
|
||||||
|
struct PacketBody {
|
||||||
|
u8 data[PacketBodySizeMax];
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename HeaderType>
|
||||||
|
class BasePacket {
|
||||||
|
private:
|
||||||
|
mem::StandardAllocator *m_allocator;
|
||||||
|
u8 *m_header;
|
||||||
|
int m_packet_size;
|
||||||
public:
|
public:
|
||||||
virtual ~Packet();
|
BasePacket(mem::StandardAllocator *allocator, int packet_size) : m_allocator(allocator), m_header(nullptr), m_packet_size(packet_size) {
|
||||||
|
AMS_ASSERT(packet_size >= static_cast<int>(sizeof(HeaderType)));
|
||||||
|
|
||||||
|
m_header = static_cast<u8 *>(m_allocator->Allocate(m_packet_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~BasePacket() {
|
||||||
|
if (m_header != nullptr) {
|
||||||
|
m_allocator->Free(m_header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAllocationSucceeded() const {
|
||||||
|
return m_header != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderType *GetHeader() {
|
||||||
|
return reinterpret_cast<HeaderType *>(m_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
const HeaderType *GetHeader() const {
|
||||||
|
return reinterpret_cast<const HeaderType *>(m_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetBodySize() const {
|
||||||
|
return m_packet_size - sizeof(HeaderType);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 *GetBody() {
|
||||||
|
if (this->GetBodySize() > 0) {
|
||||||
|
return m_header + sizeof(HeaderType);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyBody(const void *src, int src_size) {
|
||||||
|
AMS_ASSERT(this->GetBodySize() >= 0);
|
||||||
|
|
||||||
|
std::memcpy(this->GetBody(), src, src_size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Packet : public BasePacket<PacketHeader>, public util::IntrusiveListBaseNode<Packet> {
|
||||||
|
public:
|
||||||
|
using BasePacket<PacketHeader>::BasePacket;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PacketDeleter {
|
struct PacketDeleter {
|
||||||
|
@ -22,4 +22,76 @@ namespace ams::htclow {
|
|||||||
PacketDeleter{m_allocator}(packet);
|
PacketDeleter{m_allocator}(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Packet, PacketDeleter> PacketFactory::MakeSendPacketCommon(impl::ChannelInternalType channel, s16 version, int body_size) {
|
||||||
|
/* Allocate memory for the packet. */
|
||||||
|
if (void *buffer = m_allocator->Allocate(sizeof(Packet), alignof(Packet)); buffer != nullptr) {
|
||||||
|
/* Convert the buffer to a packet. */
|
||||||
|
Packet *packet = static_cast<Packet *>(buffer);
|
||||||
|
|
||||||
|
/* Construct the packet. */
|
||||||
|
std::construct_at(packet, m_allocator, body_size + sizeof(PacketHeader));
|
||||||
|
|
||||||
|
/* Create the unique pointer. */
|
||||||
|
std::unique_ptr<Packet, PacketDeleter> ptr(packet, PacketDeleter{m_allocator});
|
||||||
|
|
||||||
|
/* Set packet header fields. */
|
||||||
|
if (ptr && ptr->IsAllocationSucceeded()) {
|
||||||
|
PacketHeader *header = ptr->GetHeader();
|
||||||
|
|
||||||
|
header->signature = HtcGen2Signature;
|
||||||
|
header->offset = 0;
|
||||||
|
header->reserved = 0;
|
||||||
|
header->body_size = body_size;
|
||||||
|
header->version = version;
|
||||||
|
header->channel = channel;
|
||||||
|
header->share = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
} else {
|
||||||
|
return std::unique_ptr<Packet, PacketDeleter>(nullptr, PacketDeleter{m_allocator});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Packet, PacketDeleter> PacketFactory::MakeDataPacket(impl::ChannelInternalType channel, s16 version, const void *body, int body_size, u64 share, u32 offset) {
|
||||||
|
auto packet = this->MakeSendPacketCommon(channel, version, body_size);
|
||||||
|
if (packet) {
|
||||||
|
PacketHeader *header = packet->GetHeader();
|
||||||
|
|
||||||
|
header->packet_type = PacketType_Data;
|
||||||
|
header->offset = offset;
|
||||||
|
header->share = share;
|
||||||
|
|
||||||
|
packet->CopyBody(body, body_size);
|
||||||
|
|
||||||
|
AMS_ASSERT(packet->GetBodySize() == body_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::unique_ptr<Packet, PacketDeleter> PacketFactory::MakeMaxDataPacket(impl::ChannelInternalType channel, s16 version, u64 share) {
|
||||||
|
auto packet = this->MakeSendPacketCommon(channel, version, 0);
|
||||||
|
if (packet) {
|
||||||
|
PacketHeader *header = packet->GetHeader();
|
||||||
|
|
||||||
|
header->packet_type = PacketType_MaxData;
|
||||||
|
header->share = share;
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Packet, PacketDeleter> PacketFactory::MakeErrorPacket(impl::ChannelInternalType channel) {
|
||||||
|
auto packet = this->MakeSendPacketCommon(channel, 0, 0);
|
||||||
|
if (packet) {
|
||||||
|
PacketHeader *header = packet->GetHeader();
|
||||||
|
|
||||||
|
header->packet_type = PacketType_Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,13 @@ namespace ams::htclow {
|
|||||||
public:
|
public:
|
||||||
PacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) { /* ... */ }
|
PacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) { /* ... */ }
|
||||||
|
|
||||||
|
std::unique_ptr<Packet, PacketDeleter> MakeDataPacket(impl::ChannelInternalType channel, s16 version, const void *body, int body_size, u64 share, u32 offset);
|
||||||
|
std::unique_ptr<Packet, PacketDeleter> MakeMaxDataPacket(impl::ChannelInternalType channel, s16 version, u64 share);
|
||||||
|
std::unique_ptr<Packet, PacketDeleter> MakeErrorPacket(impl::ChannelInternalType channel);
|
||||||
|
|
||||||
void Delete(Packet *packet);
|
void Delete(Packet *packet);
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Packet, PacketDeleter> MakeSendPacketCommon(impl::ChannelInternalType channel, s16 version, int body_size);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -82,8 +82,55 @@ namespace ams::htclow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result Worker::ProcessReceive() {
|
Result Worker::ProcessReceive() {
|
||||||
/* TODO */
|
/* Forever receive packets. */
|
||||||
AMS_ABORT("Worker::ProcessReceive");
|
u8 packet_header_storage[sizeof(m_packet_header)];
|
||||||
|
while (true) {
|
||||||
|
/* Receive the packet header. */
|
||||||
|
R_TRY(m_driver->Receive(packet_header_storage, sizeof(packet_header_storage)));
|
||||||
|
|
||||||
|
/* Check if the packet is a control packet. */
|
||||||
|
if (ctrl::HtcctrlPacketHeader *ctrl_header = reinterpret_cast<ctrl::HtcctrlPacketHeader *>(packet_header_storage); ctrl_header->signature == ctrl::HtcctrlSignature) {
|
||||||
|
/* Process the packet. */
|
||||||
|
R_TRY(this->ProcessReceive(*ctrl_header));
|
||||||
|
} else {
|
||||||
|
/* Otherwise, we must have a normal packet. */
|
||||||
|
PacketHeader *header = reinterpret_cast<PacketHeader *>(packet_header_storage);
|
||||||
|
R_UNLESS(header->signature == HtcGen2Signature, htclow::ResultProtocolError());
|
||||||
|
|
||||||
|
/* Process the packet. */
|
||||||
|
R_TRY(this->ProcessReceive(*header));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Worker::ProcessReceive(const ctrl::HtcctrlPacketHeader &header) {
|
||||||
|
/* Check the header. */
|
||||||
|
R_TRY(m_service->CheckReceivedHeader(header));
|
||||||
|
|
||||||
|
/* Receive the body, if we have one. */
|
||||||
|
if (header.body_size > 0) {
|
||||||
|
R_TRY(m_driver->Receive(m_receive_packet_body, header.body_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process the received packet. */
|
||||||
|
m_service->ProcessReceivePacket(header, m_receive_packet_body, header.body_size);
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Worker::ProcessReceive(const PacketHeader &header) {
|
||||||
|
/* Check the header. */
|
||||||
|
R_TRY(m_mux->CheckReceivedHeader(header));
|
||||||
|
|
||||||
|
/* Receive the body, if we have one. */
|
||||||
|
if (header.body_size > 0) {
|
||||||
|
R_TRY(m_driver->Receive(m_receive_packet_body, header.body_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process the received packet. */
|
||||||
|
m_mux->ProcessReceivePacket(header, m_receive_packet_body, header.body_size);
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Worker::ProcessSend() {
|
Result Worker::ProcessSend() {
|
||||||
|
@ -22,9 +22,14 @@
|
|||||||
namespace ams::htclow {
|
namespace ams::htclow {
|
||||||
|
|
||||||
class Worker {
|
class Worker {
|
||||||
|
private:
|
||||||
|
static_assert(sizeof(ctrl::HtcctrlPacketHeader) <= sizeof(PacketHeader));
|
||||||
|
static_assert(sizeof(ctrl::HtcctrlPacketBody) <= sizeof(PacketBody));
|
||||||
private:
|
private:
|
||||||
u32 m_thread_stack_size;
|
u32 m_thread_stack_size;
|
||||||
u8 m_04[0x7C024]; /* TODO... not knowing what an almost 128 KB field is is embarassing. */
|
u8 m_packet_header[sizeof(PacketHeader)];
|
||||||
|
u8 m_send_packet_body[sizeof(PacketBody)];
|
||||||
|
u8 m_receive_packet_body[sizeof(PacketBody)];
|
||||||
mem::StandardAllocator *m_allocator;
|
mem::StandardAllocator *m_allocator;
|
||||||
mux::Mux *m_mux;
|
mux::Mux *m_mux;
|
||||||
ctrl::HtcctrlService *m_service;
|
ctrl::HtcctrlService *m_service;
|
||||||
@ -58,6 +63,9 @@ namespace ams::htclow {
|
|||||||
|
|
||||||
Result ProcessReceive();
|
Result ProcessReceive();
|
||||||
Result ProcessSend();
|
Result ProcessSend();
|
||||||
|
|
||||||
|
Result ProcessReceive(const ctrl::HtcctrlPacketHeader &header);
|
||||||
|
Result ProcessReceive(const PacketHeader &header);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,46 @@ namespace ams::htclow::mux {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (m_channel_impl_map.Exists(header.channel)) {
|
||||||
|
R_TRY(this->CheckChannelExist(header.channel));
|
||||||
|
|
||||||
|
return m_channel_impl_map[header.channel].ProcessReceivePacket(header, body, body_size);
|
||||||
|
} else {
|
||||||
|
if (header.packet_type == PacketType_Data || header.packet_type == PacketType_MaxData) {
|
||||||
|
this->SendErrorPacket(header.channel);
|
||||||
|
}
|
||||||
|
return htclow::ResultChannelNotExist();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Mux::UpdateChannelState() {
|
void Mux::UpdateChannelState() {
|
||||||
/* Lock ourselves. */
|
/* Lock ourselves. */
|
||||||
std::scoped_lock lk(m_mutex);
|
std::scoped_lock lk(m_mutex);
|
||||||
@ -62,4 +102,14 @@ namespace ams::htclow::mux {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result Mux::CheckChannelExist(impl::ChannelInternalType channel) {
|
||||||
|
R_UNLESS(m_channel_impl_map.Exists(channel), htclow::ResultChannelNotExist());
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Mux::SendErrorPacket(impl::ChannelInternalType channel) {
|
||||||
|
/* TODO */
|
||||||
|
AMS_ABORT("Mux::SendErrorPacket");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,15 @@ namespace ams::htclow::mux {
|
|||||||
|
|
||||||
void SetVersion(u16 version);
|
void SetVersion(u16 version);
|
||||||
|
|
||||||
|
Result CheckReceivedHeader(const PacketHeader &header) const;
|
||||||
|
Result ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size);
|
||||||
|
|
||||||
void UpdateChannelState();
|
void UpdateChannelState();
|
||||||
void UpdateMuxState();
|
void UpdateMuxState();
|
||||||
|
private:
|
||||||
|
Result CheckChannelExist(impl::ChannelInternalType channel);
|
||||||
|
|
||||||
|
Result SendErrorPacket(impl::ChannelInternalType channel);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,110 @@ namespace ams::htclow::mux {
|
|||||||
m_send_buffer.SetVersion(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) {
|
||||||
|
return htclow::ResultInvalidChannelStateDisconnected();
|
||||||
|
} else {
|
||||||
|
return htclow::ResultInvalidChannelState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ChannelImpl::CheckPacketVersion(s16 version) const {
|
||||||
|
R_UNLESS(version == m_version, htclow::ResultChannelVersionNotMatched());
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Result ChannelImpl::ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size) {
|
||||||
|
switch (header.packet_type) {
|
||||||
|
case PacketType_Data:
|
||||||
|
return this->ProcessReceiveDataPacket(header.version, header.share, header.offset, body, body_size);
|
||||||
|
case PacketType_MaxData:
|
||||||
|
return this->ProcessReceiveMaxDataPacket(header.version, header.share);
|
||||||
|
case PacketType_Error:
|
||||||
|
return this->ProcessReceiveErrorPacket();
|
||||||
|
default:
|
||||||
|
return 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());
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ChannelImpl::ProcessReceiveErrorPacket() {
|
||||||
|
if (m_state == ChannelState_Connected || m_state == ChannelState_Disconnected) {
|
||||||
|
this->ShutdownForce();
|
||||||
|
}
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
void ChannelImpl::UpdateState() {
|
void ChannelImpl::UpdateState() {
|
||||||
/* Check if shutdown must be forced. */
|
/* Check if shutdown must be forced. */
|
||||||
if (m_state_machine->IsUnsupportedServiceChannelToShutdown(m_channel)) {
|
if (m_state_machine->IsUnsupportedServiceChannelToShutdown(m_channel)) {
|
||||||
@ -83,4 +187,10 @@ namespace ams::htclow::mux {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChannelImpl::SignalSendPacketEvent() {
|
||||||
|
if (m_event != nullptr) {
|
||||||
|
m_event->Signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,10 @@ namespace ams::htclow::mux {
|
|||||||
SendBuffer m_send_buffer;
|
SendBuffer m_send_buffer;
|
||||||
RingBuffer m_receive_buffer;
|
RingBuffer m_receive_buffer;
|
||||||
s16 m_version;
|
s16 m_version;
|
||||||
/* TODO: Channel config */
|
ChannelConfig m_config;
|
||||||
/* TODO: tracking variables. */
|
/* TODO: tracking variables. */
|
||||||
std::optional<u64> m_108;
|
u64 m_offset;
|
||||||
|
std::optional<u64> m_share;
|
||||||
os::Event m_state_change_event;
|
os::Event m_state_change_event;
|
||||||
ChannelState m_state;
|
ChannelState m_state;
|
||||||
public:
|
public:
|
||||||
@ -52,11 +53,22 @@ namespace ams::htclow::mux {
|
|||||||
|
|
||||||
void SetVersion(s16 version);
|
void SetVersion(s16 version);
|
||||||
|
|
||||||
|
Result ProcessReceivePacket(const PacketHeader &header, const void *body, size_t body_size);
|
||||||
|
|
||||||
void UpdateState();
|
void UpdateState();
|
||||||
private:
|
private:
|
||||||
void ShutdownForce();
|
void ShutdownForce();
|
||||||
void SetState(ChannelState state);
|
void SetState(ChannelState state);
|
||||||
void SetStateWithoutCheck(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();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,10 @@ namespace ams::htclow::mux {
|
|||||||
ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev);
|
ChannelImplMap(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev);
|
||||||
|
|
||||||
ChannelImpl &GetChannelImpl(impl::ChannelInternalType channel);
|
ChannelImpl &GetChannelImpl(impl::ChannelInternalType channel);
|
||||||
|
|
||||||
|
bool Exists(impl::ChannelInternalType channel) const {
|
||||||
|
return m_map.find(channel) != m_map.end();
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
ChannelImpl &GetChannelImpl(int index);
|
ChannelImpl &GetChannelImpl(int index);
|
||||||
public:
|
public:
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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_mux_ring_buffer.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow::mux {
|
||||||
|
|
||||||
|
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 = m_buffer_size - pos;
|
||||||
|
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;
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -29,6 +29,10 @@ namespace ams::htclow::mux {
|
|||||||
bool m_has_copied;
|
bool m_has_copied;
|
||||||
public:
|
public:
|
||||||
RingBuffer() : m_buffer(), m_read_only_buffer(), m_is_read_only(true), m_buffer_size(), m_data_size(), m_offset(), m_has_copied(false) { /* ... */ }
|
RingBuffer() : m_buffer(), m_read_only_buffer(), m_is_read_only(true), m_buffer_size(), m_data_size(), m_offset(), m_has_copied(false) { /* ... */ }
|
||||||
|
|
||||||
|
size_t GetDataSize() { return m_data_size; }
|
||||||
|
|
||||||
|
Result Write(const void *data, size_t size);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,14 @@ namespace ams::htclow::mux {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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].size <= size) {
|
||||||
|
this->CompleteTask(i, EventTrigger_ReceiveData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TaskManager::NotifyConnectReady() {
|
void TaskManager::NotifyConnectReady() {
|
||||||
for (auto i = 0; i < MaxTaskCount; ++i) {
|
for (auto i = 0; i < MaxTaskCount; ++i) {
|
||||||
if (m_valid[i] && m_tasks[i].type == TaskType_Connect) {
|
if (m_valid[i] && m_tasks[i].type == TaskType_Connect) {
|
||||||
|
@ -22,6 +22,7 @@ namespace ams::htclow::mux {
|
|||||||
|
|
||||||
enum EventTrigger : u8 {
|
enum EventTrigger : u8 {
|
||||||
EventTrigger_Disconnect = 1,
|
EventTrigger_Disconnect = 1,
|
||||||
|
EventTrigger_ReceiveData = 2,
|
||||||
EventTrigger_ConnectReady = 11,
|
EventTrigger_ConnectReady = 11,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ namespace ams::htclow::mux {
|
|||||||
bool has_event_trigger;
|
bool has_event_trigger;
|
||||||
EventTrigger event_trigger;
|
EventTrigger event_trigger;
|
||||||
TaskType type;
|
TaskType type;
|
||||||
u64 _38;
|
size_t size;
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
bool m_valid[MaxTaskCount];
|
bool m_valid[MaxTaskCount];
|
||||||
@ -49,6 +50,7 @@ namespace ams::htclow::mux {
|
|||||||
TaskManager() : m_valid() { /* ... */ }
|
TaskManager() : m_valid() { /* ... */ }
|
||||||
|
|
||||||
void NotifyDisconnect(impl::ChannelInternalType channel);
|
void NotifyDisconnect(impl::ChannelInternalType channel);
|
||||||
|
void NotifyReceiveData(impl::ChannelInternalType channel, size_t size);
|
||||||
void NotifyConnectReady();
|
void NotifyConnectReady();
|
||||||
private:
|
private:
|
||||||
void CompleteTask(int index, EventTrigger trigger);
|
void CompleteTask(int index, EventTrigger trigger);
|
||||||
|
@ -20,13 +20,26 @@ namespace ams::htclow {
|
|||||||
|
|
||||||
R_DEFINE_NAMESPACE_RESULT_MODULE(29);
|
R_DEFINE_NAMESPACE_RESULT_MODULE(29);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(UnknownDriverType, 3);
|
R_DEFINE_ERROR_RESULT(UnknownDriverType, 3);
|
||||||
|
R_DEFINE_ERROR_RESULT(ChannelNotExist, 10);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RESULT(InvalidChannelState, 200);
|
||||||
|
R_DEFINE_ERROR_RESULT(InvalidChannelStateDisconnected, 201);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RANGE(InternalError, 1000, 2999);
|
R_DEFINE_ERROR_RANGE(InternalError, 1000, 2999);
|
||||||
R_DEFINE_ERROR_RESULT(OutOfMemory, 1002);
|
R_DEFINE_ERROR_RESULT(OutOfMemory, 1002);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidArgument, 1003);
|
R_DEFINE_ERROR_RESULT(InvalidArgument, 1003);
|
||||||
|
R_DEFINE_ERROR_RESULT(ProtocolError, 1004);
|
||||||
R_DEFINE_ERROR_RESULT(Cancelled, 1005);
|
R_DEFINE_ERROR_RESULT(Cancelled, 1005);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RANGE(MuxError, 1100, 1199);
|
||||||
|
R_DEFINE_ERROR_RESULT(ChannelBufferOverflow, 1101);
|
||||||
|
R_DEFINE_ERROR_RESULT(ChannelBufferHasNotEnoughData, 1102);
|
||||||
|
R_DEFINE_ERROR_RESULT(ChannelVersionNotMatched, 1103);
|
||||||
|
R_DEFINE_ERROR_RESULT(ChannelStateTransitionError, 1104);
|
||||||
|
R_DEFINE_ERROR_RESULT(ChannelReceiveBufferEmpty, 1106);
|
||||||
|
R_DEFINE_ERROR_RESULT(ChannelSequenceIdNotMatched, 1107);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RANGE(DriverError, 1200, 1999);
|
R_DEFINE_ERROR_RANGE(DriverError, 1200, 1999);
|
||||||
R_DEFINE_ERROR_RESULT(DriverOpened, 1201);
|
R_DEFINE_ERROR_RESULT(DriverOpened, 1201);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user