2021-02-08 07:37:16 +00:00
|
|
|
/*
|
|
|
|
* 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_state_machine.hpp"
|
2021-02-09 07:44:30 +00:00
|
|
|
#include "htclow_ctrl_service_channels.hpp"
|
2021-02-08 07:37:16 +00:00
|
|
|
|
|
|
|
namespace ams::htclow::ctrl {
|
|
|
|
|
2021-02-08 14:48:30 +00:00
|
|
|
HtcctrlStateMachine::HtcctrlStateMachine() : m_map(), m_state(HtcctrlState_DriverDisconnected), m_prev_state(HtcctrlState_DriverDisconnected), m_mutex() {
|
2021-02-08 07:37:16 +00:00
|
|
|
/* Lock ourselves. */
|
|
|
|
std::scoped_lock lk(m_mutex);
|
|
|
|
|
|
|
|
/* Initialize our map. */
|
|
|
|
m_map.Initialize(MaxChannelCount, m_map_buffer, sizeof(m_map_buffer));
|
|
|
|
|
|
|
|
/* Insert each service channel the map. */
|
|
|
|
for (const auto &channel : ServiceChannels) {
|
|
|
|
m_map.insert(std::make_pair<impl::ChannelInternalType, ServiceChannelState>(impl::ChannelInternalType{channel}, ServiceChannelState{}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-08 13:45:23 +00:00
|
|
|
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. */
|
2021-02-09 07:44:30 +00:00
|
|
|
R_UNLESS(ctrl::IsStateTransitionAllowed(m_state, state), htclow::ResultHtcctrlStateTransitionNotAllowed());
|
2021-02-08 13:45:23 +00:00
|
|
|
|
|
|
|
/* 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();
|
|
|
|
}
|
|
|
|
|
2021-02-10 03:07:51 +00:00
|
|
|
bool HtcctrlStateMachine::IsInformationNeeded() {
|
|
|
|
/* Lock ourselves. */
|
|
|
|
std::scoped_lock lk(m_mutex);
|
|
|
|
|
|
|
|
return !ctrl::IsDisconnected(m_state) && m_state != HtcctrlState_DriverConnected;
|
|
|
|
}
|
|
|
|
|
2021-02-10 04:43:40 +00:00
|
|
|
bool HtcctrlStateMachine::IsDisconnectionNeeded() {
|
|
|
|
/* Lock ourselves. */
|
|
|
|
std::scoped_lock lk(m_mutex);
|
|
|
|
|
|
|
|
return !ctrl::IsDisconnected(m_state) && m_state != HtcctrlState_Sleep && m_state != HtcctrlState_DriverConnected;
|
|
|
|
}
|
|
|
|
|
2021-02-08 13:45:23 +00:00
|
|
|
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 = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 07:44:30 +00:00
|
|
|
bool HtcctrlStateMachine::IsPossibleToSendReady() {
|
|
|
|
/* Lock ourselves. */
|
|
|
|
std::scoped_lock lk(m_mutex);
|
|
|
|
|
|
|
|
return m_state == HtcctrlState_SentReadyFromHost && this->AreServiceChannelsConnecting();
|
|
|
|
}
|
|
|
|
|
2021-02-08 13:45:23 +00:00
|
|
|
bool HtcctrlStateMachine::IsUnsupportedServiceChannelToShutdown(const impl::ChannelInternalType &channel) {
|
|
|
|
/* Lock ourselves. */
|
|
|
|
std::scoped_lock lk(m_mutex);
|
|
|
|
|
|
|
|
auto it = m_map.find(channel);
|
2021-02-08 14:48:30 +00:00
|
|
|
return it != m_map.end() && it->second.connect == ServiceChannelConnect_ConnectingChecked && it->second.support == ServiceChannelSupport_Unsupported;
|
2021-02-08 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool HtcctrlStateMachine::IsConnectable(const impl::ChannelInternalType &channel) {
|
|
|
|
/* Lock ourselves. */
|
|
|
|
std::scoped_lock lk(m_mutex);
|
|
|
|
|
|
|
|
auto it = m_map.find(channel);
|
2021-02-08 14:48:30 +00:00
|
|
|
return ctrl::IsConnected(m_state) && (it == m_map.end() || it->second.connect != ServiceChannelConnect_ConnectingChecked);
|
2021-02-08 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
2021-02-10 03:07:51 +00:00
|
|
|
void HtcctrlStateMachine::SetConnecting(const impl::ChannelInternalType &channel) {
|
|
|
|
/* Lock ourselves. */
|
|
|
|
std::scoped_lock lk(m_mutex);
|
|
|
|
|
|
|
|
auto it = m_map.find(channel);
|
|
|
|
if (it != m_map.end() && it->second.connect != ServiceChannelConnect_ConnectingChecked) {
|
|
|
|
it->second.connect = ServiceChannelConnect_Connecting;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-08 13:45:23 +00:00
|
|
|
void HtcctrlStateMachine::SetNotConnecting(const impl::ChannelInternalType &channel) {
|
|
|
|
/* Lock ourselves. */
|
|
|
|
std::scoped_lock lk(m_mutex);
|
|
|
|
|
|
|
|
auto it = m_map.find(channel);
|
2021-02-08 14:48:30 +00:00
|
|
|
if (it != m_map.end() && it->second.connect != ServiceChannelConnect_ConnectingChecked) {
|
|
|
|
it->second.connect = ServiceChannelConnect_NotConnecting;
|
2021-02-08 13:45:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 07:44:30 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-02-08 07:37:16 +00:00
|
|
|
}
|