htc: implement htclow listener thread

This commit is contained in:
Michael Scire 2021-02-08 05:45:23 -08:00 committed by SciresM
parent c9c41e0e8d
commit 2341f18edd
21 changed files with 669 additions and 11 deletions

View file

@ -15,6 +15,8 @@
*/
#include <stratosphere.hpp>
#include "htclow_ctrl_service.hpp"
#include "htclow_ctrl_state.hpp"
#include "htclow_ctrl_state_machine.hpp"
#include "../mux/htclow_mux.hpp"
namespace ams::htclow::ctrl {
@ -81,4 +83,58 @@ namespace ams::htclow::ctrl {
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();
}
}

View file

@ -17,6 +17,7 @@
#include <stratosphere.hpp>
#include "htclow_ctrl_settings_holder.hpp"
#include "htclow_ctrl_send_buffer.hpp"
#include "htclow_ctrl_state.hpp"
namespace ams::htclow {
@ -55,10 +56,16 @@ namespace ams::htclow::ctrl {
const char *GetConnectionType(impl::DriverType driver_type) const;
void UpdateBeaconResponse(const char *connection);
Result SetState(HtcctrlState state);
void ReflectState();
public:
HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux);
void SetDriverType(impl::DriverType driver_type);
Result NotifyDriverConnected();
Result NotifyDriverDisconnected();
};
}

View file

@ -19,7 +19,115 @@
namespace ams::htclow::ctrl {
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;
}
}
}

View file

@ -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. */
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;
}
}
}

View file

@ -39,6 +39,27 @@ namespace ams::htclow::ctrl {
os::SdkMutex m_mutex;
public:
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();
};
}