mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-10 07:06:34 +00:00
htc: implement htclow listener thread
This commit is contained in:
parent
c9c41e0e8d
commit
2341f18edd
21 changed files with 669 additions and 11 deletions
|
@ -27,4 +27,25 @@ namespace ams::htclow {
|
||||||
ChannelId _channel_id;
|
ChannelId _channel_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ChannelState {
|
||||||
|
ChannelState_Connectable = 0,
|
||||||
|
ChannelState_Unconnectable = 1,
|
||||||
|
ChannelState_Connected = 2,
|
||||||
|
ChannelState_Shutdown = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr bool IsStateTransitionAllowed(ChannelState from, ChannelState to) {
|
||||||
|
switch (from) {
|
||||||
|
case ChannelState_Connectable:
|
||||||
|
return to == ChannelState_Unconnectable || to == ChannelState_Connected || to == ChannelState_Shutdown;
|
||||||
|
case ChannelState_Unconnectable:
|
||||||
|
return to == ChannelState_Connectable || to == ChannelState_Shutdown;
|
||||||
|
case ChannelState_Connected:
|
||||||
|
return to == ChannelState_Shutdown;
|
||||||
|
case ChannelState_Shutdown:
|
||||||
|
return to == ChannelState_Shutdown;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "htclow_ctrl_service.hpp"
|
#include "htclow_ctrl_service.hpp"
|
||||||
|
#include "htclow_ctrl_state.hpp"
|
||||||
|
#include "htclow_ctrl_state_machine.hpp"
|
||||||
#include "../mux/htclow_mux.hpp"
|
#include "../mux/htclow_mux.hpp"
|
||||||
|
|
||||||
namespace ams::htclow::ctrl {
|
namespace ams::htclow::ctrl {
|
||||||
|
@ -81,4 +83,58 @@ namespace ams::htclow::ctrl {
|
||||||
this->UpdateBeaconResponse(this->GetConnectionType(driver_type));
|
this->UpdateBeaconResponse(this->GetConnectionType(driver_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result HtcctrlService::NotifyDriverConnected() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
if (m_state_machine->GetHtcctrlState() == HtcctrlState_7) {
|
||||||
|
R_TRY(this->SetState(HtcctrlState_8));
|
||||||
|
} else {
|
||||||
|
R_TRY(this->SetState(HtcctrlState_0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result HtcctrlService::NotifyDriverDisconnected() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
if (m_state_machine->GetHtcctrlState() == HtcctrlState_6) {
|
||||||
|
R_TRY(this->SetState(HtcctrlState_7));
|
||||||
|
} else {
|
||||||
|
R_TRY(this->SetState(HtcctrlState_11));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result HtcctrlService::SetState(HtcctrlState state) {
|
||||||
|
/* Set the state. */
|
||||||
|
bool did_transition;
|
||||||
|
R_TRY(m_state_machine->SetHtcctrlState(std::addressof(did_transition), state));
|
||||||
|
|
||||||
|
/* Reflect the state transition, if one occurred. */
|
||||||
|
if (did_transition) {
|
||||||
|
this->ReflectState();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HtcctrlService::ReflectState() {
|
||||||
|
/* If our connected status changed, update. */
|
||||||
|
if (m_state_machine->IsConnectedStatusChanged()) {
|
||||||
|
m_mux->UpdateChannelState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If our sleeping status changed, update. */
|
||||||
|
if (m_state_machine->IsSleepingStatusChanged()) {
|
||||||
|
m_mux->UpdateMuxState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Broadcast our state transition. */
|
||||||
|
m_condvar.Broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "htclow_ctrl_settings_holder.hpp"
|
#include "htclow_ctrl_settings_holder.hpp"
|
||||||
#include "htclow_ctrl_send_buffer.hpp"
|
#include "htclow_ctrl_send_buffer.hpp"
|
||||||
|
#include "htclow_ctrl_state.hpp"
|
||||||
|
|
||||||
namespace ams::htclow {
|
namespace ams::htclow {
|
||||||
|
|
||||||
|
@ -55,10 +56,16 @@ namespace ams::htclow::ctrl {
|
||||||
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 SetState(HtcctrlState state);
|
||||||
|
void ReflectState();
|
||||||
public:
|
public:
|
||||||
HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux);
|
HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux);
|
||||||
|
|
||||||
void SetDriverType(impl::DriverType driver_type);
|
void SetDriverType(impl::DriverType driver_type);
|
||||||
|
|
||||||
|
Result NotifyDriverConnected();
|
||||||
|
Result NotifyDriverDisconnected();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,115 @@
|
||||||
namespace ams::htclow::ctrl {
|
namespace ams::htclow::ctrl {
|
||||||
|
|
||||||
enum HtcctrlState : u32 {
|
enum HtcctrlState : u32 {
|
||||||
HtcctrlState_Eleven = 11,
|
HtcctrlState_0 = 0,
|
||||||
|
HtcctrlState_1 = 1,
|
||||||
|
HtcctrlState_2 = 2,
|
||||||
|
HtcctrlState_3 = 3,
|
||||||
|
HtcctrlState_4 = 4,
|
||||||
|
HtcctrlState_5 = 5,
|
||||||
|
HtcctrlState_6 = 6,
|
||||||
|
HtcctrlState_7 = 7,
|
||||||
|
HtcctrlState_8 = 8,
|
||||||
|
HtcctrlState_9 = 9,
|
||||||
|
HtcctrlState_10 = 10,
|
||||||
|
HtcctrlState_11 = 11,
|
||||||
|
HtcctrlState_12 = 12,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr bool IsStateTransitionAllowed(HtcctrlState from, HtcctrlState to) {
|
||||||
|
switch (from) {
|
||||||
|
case HtcctrlState_0:
|
||||||
|
return to == HtcctrlState_1 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12;
|
||||||
|
case HtcctrlState_1:
|
||||||
|
return to == HtcctrlState_2 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12;
|
||||||
|
case HtcctrlState_2:
|
||||||
|
return to == HtcctrlState_3 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12;
|
||||||
|
case HtcctrlState_3:
|
||||||
|
return to == HtcctrlState_4 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12;
|
||||||
|
case HtcctrlState_4:
|
||||||
|
return to == HtcctrlState_5 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12;
|
||||||
|
case HtcctrlState_5:
|
||||||
|
return to == HtcctrlState_6 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12;
|
||||||
|
case HtcctrlState_6:
|
||||||
|
return to == HtcctrlState_7 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12;
|
||||||
|
case HtcctrlState_7:
|
||||||
|
return to == HtcctrlState_8;
|
||||||
|
case HtcctrlState_8:
|
||||||
|
return to == HtcctrlState_9 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12;
|
||||||
|
case HtcctrlState_9:
|
||||||
|
return to == HtcctrlState_4 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12;
|
||||||
|
case HtcctrlState_10:
|
||||||
|
return to == HtcctrlState_1 || to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12;
|
||||||
|
case HtcctrlState_11:
|
||||||
|
return to == HtcctrlState_0;
|
||||||
|
case HtcctrlState_12:
|
||||||
|
return to == HtcctrlState_10 || to == HtcctrlState_11 || to == HtcctrlState_12;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsDisconnected(HtcctrlState state) {
|
||||||
|
switch (state) {
|
||||||
|
case HtcctrlState_10:
|
||||||
|
case HtcctrlState_11:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsConnecting(HtcctrlState state) {
|
||||||
|
switch (state) {
|
||||||
|
case HtcctrlState_0:
|
||||||
|
case HtcctrlState_1:
|
||||||
|
case HtcctrlState_10:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsConnected(HtcctrlState state) {
|
||||||
|
switch (state) {
|
||||||
|
case HtcctrlState_2:
|
||||||
|
case HtcctrlState_3:
|
||||||
|
case HtcctrlState_4:
|
||||||
|
case HtcctrlState_5:
|
||||||
|
case HtcctrlState_6:
|
||||||
|
case HtcctrlState_7:
|
||||||
|
case HtcctrlState_8:
|
||||||
|
case HtcctrlState_9:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsReadied(HtcctrlState state) {
|
||||||
|
switch (state) {
|
||||||
|
case HtcctrlState_4:
|
||||||
|
case HtcctrlState_5:
|
||||||
|
case HtcctrlState_6:
|
||||||
|
case HtcctrlState_7:
|
||||||
|
case HtcctrlState_8:
|
||||||
|
case HtcctrlState_9:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsSleeping(HtcctrlState state) {
|
||||||
|
switch (state) {
|
||||||
|
case HtcctrlState_5:
|
||||||
|
case HtcctrlState_6:
|
||||||
|
case HtcctrlState_7:
|
||||||
|
case HtcctrlState_8:
|
||||||
|
case HtcctrlState_9:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ namespace ams::htclow::ctrl {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_Eleven), m_prev_state(HtcctrlState_Eleven), m_mutex() {
|
HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_11), m_prev_state(HtcctrlState_11), m_mutex() {
|
||||||
/* Lock ourselves. */
|
/* Lock ourselves. */
|
||||||
std::scoped_lock lk(m_mutex);
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
@ -55,4 +55,127 @@ namespace ams::htclow::ctrl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HtcctrlState HtcctrlStateMachine::GetHtcctrlState() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
return m_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result HtcctrlStateMachine::SetHtcctrlState(bool *out_transitioned, HtcctrlState state) {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* Check that the transition is allowed. */
|
||||||
|
R_UNLESS(ctrl::IsStateTransitionAllowed(m_state, state), htclow::ResultStateTransitionNotAllowed());
|
||||||
|
|
||||||
|
/* Get the state pre-transition. */
|
||||||
|
const auto old_state = m_state;
|
||||||
|
|
||||||
|
/* Set the state. */
|
||||||
|
this->SetStateWithoutCheckInternal(state);
|
||||||
|
|
||||||
|
/* Note whether we transitioned. */
|
||||||
|
*out_transitioned = state != old_state;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HtcctrlStateMachine::IsConnected() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
return ctrl::IsConnected(m_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HtcctrlStateMachine::IsReadied() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
return ctrl::IsReadied(m_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HtcctrlStateMachine::IsUnconnectable() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
return !ctrl::IsConnected(m_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HtcctrlStateMachine::IsDisconnected() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
return ctrl::IsDisconnected(m_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HtcctrlStateMachine::IsSleeping() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
return ctrl::IsSleeping(m_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HtcctrlStateMachine::IsConnectedStatusChanged() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
return ctrl::IsConnected(m_prev_state) ^ ctrl::IsConnected(m_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HtcctrlStateMachine::IsSleepingStatusChanged() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
return ctrl::IsSleeping(m_prev_state) ^ ctrl::IsSleeping(m_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HtcctrlStateMachine::SetStateWithoutCheckInternal(HtcctrlState state) {
|
||||||
|
if (m_state != state) {
|
||||||
|
/* Clear service channel states, if we should. */
|
||||||
|
if (ctrl::IsDisconnected(state)) {
|
||||||
|
this->ClearServiceChannelStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Transition our state. */
|
||||||
|
m_prev_state = m_state;
|
||||||
|
m_state = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HtcctrlStateMachine::ClearServiceChannelStates() {
|
||||||
|
/* Clear all values in our map. */
|
||||||
|
for (auto &pair : m_map) {
|
||||||
|
pair.second = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HtcctrlStateMachine::IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel) {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* TODO: What do these values mean? */
|
||||||
|
auto it = m_map.find(channel);
|
||||||
|
return it != m_map.end() && it->second._04 == 2 && it->second._00 == 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HtcctrlStateMachine::IsConnectable(const impl::ChannelInternalType &channel) {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* TODO: What do these values mean? */
|
||||||
|
auto it = m_map.find(channel);
|
||||||
|
return ctrl::IsConnected(m_state) && (!(it != m_map.end()) || it->second._04 != 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HtcctrlStateMachine::SetNotConnecting(const impl::ChannelInternalType &channel) {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* TODO: What do these values mean? */
|
||||||
|
auto it = m_map.find(channel);
|
||||||
|
if (it != m_map.end() && it->second._04 != 2) {
|
||||||
|
it->second._04 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,27 @@ namespace ams::htclow::ctrl {
|
||||||
os::SdkMutex m_mutex;
|
os::SdkMutex m_mutex;
|
||||||
public:
|
public:
|
||||||
HtcctrlStateMachine();
|
HtcctrlStateMachine();
|
||||||
|
|
||||||
|
HtcctrlState GetHtcctrlState();
|
||||||
|
Result SetHtcctrlState(bool *out_transitioned, HtcctrlState state);
|
||||||
|
|
||||||
|
bool IsConnectedStatusChanged();
|
||||||
|
bool IsSleepingStatusChanged();
|
||||||
|
|
||||||
|
bool IsConnected();
|
||||||
|
bool IsReadied();
|
||||||
|
bool IsUnconnectable();
|
||||||
|
bool IsDisconnected();
|
||||||
|
bool IsSleeping();
|
||||||
|
|
||||||
|
bool IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel);
|
||||||
|
bool IsConnectable(const impl::ChannelInternalType &channel);
|
||||||
|
|
||||||
|
void SetNotConnecting(const impl::ChannelInternalType &channel);
|
||||||
|
private:
|
||||||
|
void SetStateWithoutCheckInternal(HtcctrlState state);
|
||||||
|
|
||||||
|
void ClearServiceChannelStates();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,8 +56,35 @@ namespace ams::htclow {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Listener::ListenThread() {
|
void Listener::ListenThread() {
|
||||||
/* TODO */
|
/* Check pre-conditions. */
|
||||||
AMS_ABORT("Listener::ListenThread");
|
AMS_ASSERT(m_driver != nullptr);
|
||||||
|
AMS_ASSERT(m_worker != nullptr);
|
||||||
|
|
||||||
|
/* Set the worker's driver. */
|
||||||
|
m_worker->SetDriver(m_driver);
|
||||||
|
|
||||||
|
/* Loop forever, while we're not cancelled. */
|
||||||
|
while (!m_cancelled) {
|
||||||
|
/* Connect. */
|
||||||
|
if (R_FAILED(m_driver->Connect(m_event.GetBase()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Notify that we're connected. */
|
||||||
|
R_ABORT_UNLESS(m_service->NotifyDriverConnected());
|
||||||
|
|
||||||
|
/* Start the worker. */
|
||||||
|
m_worker->Start();
|
||||||
|
|
||||||
|
/* Wait for the worker to end. */
|
||||||
|
m_worker->Wait();
|
||||||
|
|
||||||
|
/* Shutdown the driver. */
|
||||||
|
m_driver->Shutdown();
|
||||||
|
|
||||||
|
/* Notify that we're disconnected. */
|
||||||
|
R_ABORT_UNLESS(m_service->NotifyDriverDisconnected());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,17 @@ namespace ams::htclow {
|
||||||
|
|
||||||
class Packet : public util::IntrusiveListBaseNode<Packet> {
|
class Packet : public util::IntrusiveListBaseNode<Packet> {
|
||||||
/* TODO */
|
/* TODO */
|
||||||
|
public:
|
||||||
|
virtual ~Packet();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PacketDeleter {
|
||||||
|
mem::StandardAllocator *m_allocator;
|
||||||
|
|
||||||
|
void operator()(Packet *packet) {
|
||||||
|
std::destroy_at(packet);
|
||||||
|
m_allocator->Free(packet);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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_packet_factory.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow {
|
||||||
|
|
||||||
|
void PacketFactory::Delete(Packet *packet) {
|
||||||
|
PacketDeleter{m_allocator}(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
#include "htclow_packet.hpp"
|
||||||
|
|
||||||
namespace ams::htclow {
|
namespace ams::htclow {
|
||||||
|
|
||||||
|
@ -23,6 +24,8 @@ namespace ams::htclow {
|
||||||
mem::StandardAllocator *m_allocator;
|
mem::StandardAllocator *m_allocator;
|
||||||
public:
|
public:
|
||||||
PacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) { /* ... */ }
|
PacketFactory(mem::StandardAllocator *allocator) : m_allocator(allocator) { /* ... */ }
|
||||||
|
|
||||||
|
void Delete(Packet *packet);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,77 @@
|
||||||
|
|
||||||
namespace ams::htclow {
|
namespace ams::htclow {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline size_t ThreadStackSize = 4_KB;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Worker::Worker(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv)
|
Worker::Worker(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv)
|
||||||
: m_thread_stack_size(4_KB), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_driver(nullptr), m_event(os::EventClearMode_ManualClear), m_7C400()
|
: m_thread_stack_size(ThreadStackSize), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_driver(nullptr), m_event(os::EventClearMode_ManualClear), m_cancelled(false)
|
||||||
{
|
{
|
||||||
/* Allocate stacks. */
|
/* Allocate stacks. */
|
||||||
m_receive_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment);
|
m_receive_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment);
|
||||||
m_send_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment);
|
m_send_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Worker::SetDriver(driver::IDriver *driver) {
|
||||||
|
m_driver = driver;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Worker::Start() {
|
||||||
|
/* Clear our cancelled state. */
|
||||||
|
m_cancelled = false;
|
||||||
|
|
||||||
|
/* Clear our event. */
|
||||||
|
m_event.Clear();
|
||||||
|
|
||||||
|
/* Create our threads. */
|
||||||
|
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_receive_thread), ReceiveThreadEntry, this, m_receive_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowReceive)));
|
||||||
|
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_send_thread), SendThreadEntry, this, m_send_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowSend)));
|
||||||
|
|
||||||
|
/* Set thread names. */
|
||||||
|
os::SetThreadNamePointer(std::addressof(m_receive_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowReceive));
|
||||||
|
os::SetThreadNamePointer(std::addressof(m_send_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowSend));
|
||||||
|
|
||||||
|
/* Start our threads. */
|
||||||
|
os::StartThread(std::addressof(m_receive_thread));
|
||||||
|
os::StartThread(std::addressof(m_send_thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Worker::Wait() {
|
||||||
|
os::WaitThread(std::addressof(m_receive_thread));
|
||||||
|
os::WaitThread(std::addressof(m_send_thread));
|
||||||
|
os::DestroyThread(std::addressof(m_receive_thread));
|
||||||
|
os::DestroyThread(std::addressof(m_send_thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Worker::ReceiveThread() {
|
||||||
|
this->ProcessReceive();
|
||||||
|
m_driver->CancelSendReceive();
|
||||||
|
this->Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Worker::SendThread() {
|
||||||
|
this->ProcessSend();
|
||||||
|
m_driver->CancelSendReceive();
|
||||||
|
this->Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Worker::Cancel() {
|
||||||
|
/* Set ourselves as cancelled, and signal. */
|
||||||
|
m_cancelled = true;
|
||||||
|
m_event.Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Worker::ProcessReceive() {
|
||||||
|
/* TODO */
|
||||||
|
AMS_ABORT("Worker::ProcessReceive");
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Worker::ProcessSend() {
|
||||||
|
/* TODO */
|
||||||
|
AMS_ABORT("Worker::ProcessSend");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,30 @@ namespace ams::htclow {
|
||||||
os::Event m_event;
|
os::Event m_event;
|
||||||
void *m_receive_thread_stack;
|
void *m_receive_thread_stack;
|
||||||
void *m_send_thread_stack;
|
void *m_send_thread_stack;
|
||||||
u8 m_7C400;
|
bool m_cancelled;
|
||||||
|
private:
|
||||||
|
static void ReceiveThreadEntry(void *arg) {
|
||||||
|
static_cast<Worker *>(arg)->ReceiveThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SendThreadEntry(void *arg) {
|
||||||
|
static_cast<Worker *>(arg)->SendThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReceiveThread();
|
||||||
|
void SendThread();
|
||||||
public:
|
public:
|
||||||
Worker(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv);
|
Worker(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv);
|
||||||
|
|
||||||
|
void SetDriver(driver::IDriver *driver);
|
||||||
|
|
||||||
|
void Start();
|
||||||
|
void Wait();
|
||||||
|
private:
|
||||||
|
void Cancel();
|
||||||
|
|
||||||
|
Result ProcessReceive();
|
||||||
|
Result ProcessSend();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "htclow_mux.hpp"
|
#include "htclow_mux.hpp"
|
||||||
|
#include "../ctrl/htclow_ctrl_state_machine.hpp"
|
||||||
|
|
||||||
namespace ams::htclow::mux {
|
namespace ams::htclow::mux {
|
||||||
|
|
||||||
|
@ -37,4 +38,28 @@ namespace ams::htclow::mux {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mux::UpdateChannelState() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* Update the state of all channels in our map. */
|
||||||
|
/* NOTE: Nintendo does this highly inefficiently... */
|
||||||
|
for (auto pair : m_channel_impl_map.GetMap()) {
|
||||||
|
m_channel_impl_map[pair.first].UpdateState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mux::UpdateMuxState() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* Update whether we're sleeping. */
|
||||||
|
if (m_state_machine->IsSleeping()) {
|
||||||
|
m_is_sleeping = true;
|
||||||
|
} else {
|
||||||
|
m_is_sleeping = false;
|
||||||
|
m_wake_event.Signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,9 @@ namespace ams::htclow::mux {
|
||||||
Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm);
|
Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm);
|
||||||
|
|
||||||
void SetVersion(u16 version);
|
void SetVersion(u16 version);
|
||||||
|
|
||||||
|
void UpdateChannelState();
|
||||||
|
void UpdateMuxState();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "htclow_mux_channel_impl.hpp"
|
#include "htclow_mux_channel_impl.hpp"
|
||||||
|
#include "../ctrl/htclow_ctrl_state_machine.hpp"
|
||||||
|
|
||||||
namespace ams::htclow::mux {
|
namespace ams::htclow::mux {
|
||||||
|
|
||||||
|
@ -27,4 +28,59 @@ namespace ams::htclow::mux {
|
||||||
m_send_buffer.SetVersion(version);
|
m_send_buffer.SetVersion(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChannelImpl::UpdateState() {
|
||||||
|
/* Check if shutdown must be forced. */
|
||||||
|
if (m_state_machine->IsUnsupportedServiceChannelToShutdown(m_channel)) {
|
||||||
|
this->ShutdownForce();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we're readied. */
|
||||||
|
if (m_state_machine->IsReadied()) {
|
||||||
|
m_task_manager->NotifyConnectReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update our state transition. */
|
||||||
|
if (m_state_machine->IsConnectable(m_channel)) {
|
||||||
|
if (m_state == ChannelState_Unconnectable) {
|
||||||
|
this->SetState(ChannelState_Connectable);
|
||||||
|
}
|
||||||
|
} else if (m_state_machine->IsUnconnectable()) {
|
||||||
|
if (m_state == ChannelState_Connectable) {
|
||||||
|
this->SetState(ChannelState_Unconnectable);
|
||||||
|
m_state_machine->SetNotConnecting(m_channel);
|
||||||
|
} else if (m_state == ChannelState_Connected) {
|
||||||
|
this->ShutdownForce();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelImpl::ShutdownForce() {
|
||||||
|
/* Clear our send buffer. */
|
||||||
|
m_send_buffer.Clear();
|
||||||
|
|
||||||
|
/* Set our state to shutdown. */
|
||||||
|
this->SetState(ChannelState_Shutdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelImpl::SetState(ChannelState state) {
|
||||||
|
/* Check that we can perform the transition. */
|
||||||
|
AMS_ABORT_UNLESS(IsStateTransitionAllowed(m_state, state));
|
||||||
|
|
||||||
|
/* Perform the transition. */
|
||||||
|
this->SetStateWithoutCheck(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelImpl::SetStateWithoutCheck(ChannelState state) {
|
||||||
|
/* Change our state. */
|
||||||
|
if (m_state != state) {
|
||||||
|
m_state = state;
|
||||||
|
m_state_change_event.Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If relevant, notify disconnect. */
|
||||||
|
if (m_state == ChannelState_Shutdown) {
|
||||||
|
m_task_manager->NotifyDisconnect(m_channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,12 +45,18 @@ namespace ams::htclow::mux {
|
||||||
/* TODO: Channel config */
|
/* TODO: Channel config */
|
||||||
/* TODO: tracking variables. */
|
/* TODO: tracking variables. */
|
||||||
std::optional<u64> m_108;
|
std::optional<u64> m_108;
|
||||||
os::Event m_send_packet_event;
|
os::Event m_state_change_event;
|
||||||
/* TODO: Channel state. */
|
ChannelState m_state;
|
||||||
public:
|
public:
|
||||||
ChannelImpl(impl::ChannelInternalType channel, PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev);
|
ChannelImpl(impl::ChannelInternalType channel, PacketFactory *pf, ctrl::HtcctrlStateMachine *sm, TaskManager *tm, os::Event *ev);
|
||||||
|
|
||||||
void SetVersion(s16 version);
|
void SetVersion(s16 version);
|
||||||
|
|
||||||
|
void UpdateState();
|
||||||
|
private:
|
||||||
|
void ShutdownForce();
|
||||||
|
void SetState(ChannelState state);
|
||||||
|
void SetStateWithoutCheck(ChannelState state);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "htclow_mux_channel_impl.hpp"
|
#include "htclow_mux_channel_impl.hpp"
|
||||||
|
#include "../htclow_packet_factory.hpp"
|
||||||
|
|
||||||
namespace ams::htclow::mux {
|
namespace ams::htclow::mux {
|
||||||
|
|
||||||
|
@ -23,4 +24,12 @@ namespace ams::htclow::mux {
|
||||||
m_version = version;
|
m_version = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendBuffer::Clear() {
|
||||||
|
while (!m_packet_list.empty()) {
|
||||||
|
auto *packet = std::addressof(m_packet_list.front());
|
||||||
|
m_packet_list.pop_front();
|
||||||
|
m_packet_factory->Delete(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,8 @@ namespace ams::htclow::mux {
|
||||||
SendBuffer(impl::ChannelInternalType channel, PacketFactory *pf);
|
SendBuffer(impl::ChannelInternalType channel, PacketFactory *pf);
|
||||||
|
|
||||||
void SetVersion(s16 version);
|
void SetVersion(s16 version);
|
||||||
|
|
||||||
|
void Clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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_task_manager.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow::mux {
|
||||||
|
|
||||||
|
void TaskManager::NotifyDisconnect(impl::ChannelInternalType channel) {
|
||||||
|
for (auto i = 0; i < MaxTaskCount; ++i) {
|
||||||
|
if (m_valid[i] && m_tasks[i].channel == channel) {
|
||||||
|
this->CompleteTask(i, EventTrigger_Disconnect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskManager::NotifyConnectReady() {
|
||||||
|
for (auto i = 0; i < MaxTaskCount; ++i) {
|
||||||
|
if (m_valid[i] && m_tasks[i].type == TaskType_Connect) {
|
||||||
|
this->CompleteTask(i, EventTrigger_ConnectReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskManager::CompleteTask(int index, EventTrigger trigger) {
|
||||||
|
/* Check pre-conditions. */
|
||||||
|
AMS_ASSERT(0 <= index && index < MaxTaskCount);
|
||||||
|
AMS_ASSERT(m_valid[index]);
|
||||||
|
|
||||||
|
/* Complete the task. */
|
||||||
|
if (!m_tasks[index].has_event_trigger) {
|
||||||
|
m_tasks[index].has_event_trigger = true;
|
||||||
|
m_tasks[index].event_trigger = trigger;
|
||||||
|
os::SignalEvent(std::addressof(m_tasks[index].event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,14 +20,26 @@ namespace ams::htclow::mux {
|
||||||
|
|
||||||
constexpr inline int MaxTaskCount = 0x80;
|
constexpr inline int MaxTaskCount = 0x80;
|
||||||
|
|
||||||
|
enum EventTrigger : u8 {
|
||||||
|
EventTrigger_Disconnect = 1,
|
||||||
|
EventTrigger_ConnectReady = 11,
|
||||||
|
};
|
||||||
|
|
||||||
class TaskManager {
|
class TaskManager {
|
||||||
private:
|
private:
|
||||||
|
enum TaskType : u8 {
|
||||||
|
TaskType_Receive = 0,
|
||||||
|
TaskType_Send = 1,
|
||||||
|
TaskType_Flush = 6,
|
||||||
|
TaskType_Connect = 7,
|
||||||
|
};
|
||||||
|
|
||||||
struct Task {
|
struct Task {
|
||||||
impl::ChannelInternalType channel;
|
impl::ChannelInternalType channel;
|
||||||
os::EventType event;
|
os::EventType event;
|
||||||
u8 _30;
|
bool has_event_trigger;
|
||||||
u8 _31;
|
EventTrigger event_trigger;
|
||||||
u8 _32;
|
TaskType type;
|
||||||
u64 _38;
|
u64 _38;
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
|
@ -35,6 +47,11 @@ namespace ams::htclow::mux {
|
||||||
Task m_tasks[MaxTaskCount];
|
Task m_tasks[MaxTaskCount];
|
||||||
public:
|
public:
|
||||||
TaskManager() : m_valid() { /* ... */ }
|
TaskManager() : m_valid() { /* ... */ }
|
||||||
|
|
||||||
|
void NotifyDisconnect(impl::ChannelInternalType channel);
|
||||||
|
void NotifyConnectReady();
|
||||||
|
private:
|
||||||
|
void CompleteTask(int index, EventTrigger trigger);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,4 +36,6 @@ 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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue